Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/click/decorators.py: 16%

189 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1import inspect 

2import types 

3import typing as t 

4from functools import update_wrapper 

5from gettext import gettext as _ 

6 

7from .core import Argument 

8from .core import Command 

9from .core import Context 

10from .core import Group 

11from .core import Option 

12from .core import Parameter 

13from .globals import get_current_context 

14from .utils import echo 

15 

16F = t.TypeVar("F", bound=t.Callable[..., t.Any]) 

17FC = t.TypeVar("FC", bound=t.Union[t.Callable[..., t.Any], Command]) 

18 

19 

20def pass_context(f: F) -> F: 

21 """Marks a callback as wanting to receive the current context 

22 object as first argument. 

23 """ 

24 

25 def new_func(*args, **kwargs): # type: ignore 

26 return f(get_current_context(), *args, **kwargs) 

27 

28 return update_wrapper(t.cast(F, new_func), f) 

29 

30 

31def pass_obj(f: F) -> F: 

32 """Similar to :func:`pass_context`, but only pass the object on the 

33 context onwards (:attr:`Context.obj`). This is useful if that object 

34 represents the state of a nested system. 

35 """ 

36 

37 def new_func(*args, **kwargs): # type: ignore 

38 return f(get_current_context().obj, *args, **kwargs) 

39 

40 return update_wrapper(t.cast(F, new_func), f) 

41 

42 

43def make_pass_decorator( 

44 object_type: t.Type, ensure: bool = False 

45) -> "t.Callable[[F], F]": 

46 """Given an object type this creates a decorator that will work 

47 similar to :func:`pass_obj` but instead of passing the object of the 

48 current context, it will find the innermost context of type 

49 :func:`object_type`. 

50 

51 This generates a decorator that works roughly like this:: 

52 

53 from functools import update_wrapper 

54 

55 def decorator(f): 

56 @pass_context 

57 def new_func(ctx, *args, **kwargs): 

58 obj = ctx.find_object(object_type) 

59 return ctx.invoke(f, obj, *args, **kwargs) 

60 return update_wrapper(new_func, f) 

61 return decorator 

62 

63 :param object_type: the type of the object to pass. 

64 :param ensure: if set to `True`, a new object will be created and 

65 remembered on the context if it's not there yet. 

66 """ 

67 

68 def decorator(f: F) -> F: 

69 def new_func(*args, **kwargs): # type: ignore 

70 ctx = get_current_context() 

71 

72 if ensure: 

73 obj = ctx.ensure_object(object_type) 

74 else: 

75 obj = ctx.find_object(object_type) 

76 

77 if obj is None: 

78 raise RuntimeError( 

79 "Managed to invoke callback without a context" 

80 f" object of type {object_type.__name__!r}" 

81 " existing." 

82 ) 

83 

84 return ctx.invoke(f, obj, *args, **kwargs) 

85 

86 return update_wrapper(t.cast(F, new_func), f) 

87 

88 return decorator 

89 

90 

91def pass_meta_key( 

92 key: str, *, doc_description: t.Optional[str] = None 

93) -> "t.Callable[[F], F]": 

94 """Create a decorator that passes a key from 

95 :attr:`click.Context.meta` as the first argument to the decorated 

96 function. 

97 

98 :param key: Key in ``Context.meta`` to pass. 

99 :param doc_description: Description of the object being passed, 

100 inserted into the decorator's docstring. Defaults to "the 'key' 

101 key from Context.meta". 

102 

103 .. versionadded:: 8.0 

104 """ 

105 

106 def decorator(f: F) -> F: 

107 def new_func(*args, **kwargs): # type: ignore 

108 ctx = get_current_context() 

109 obj = ctx.meta[key] 

110 return ctx.invoke(f, obj, *args, **kwargs) 

111 

112 return update_wrapper(t.cast(F, new_func), f) 

113 

114 if doc_description is None: 

115 doc_description = f"the {key!r} key from :attr:`click.Context.meta`" 

116 

117 decorator.__doc__ = ( 

118 f"Decorator that passes {doc_description} as the first argument" 

119 " to the decorated function." 

120 ) 

121 return decorator 

122 

123 

124CmdType = t.TypeVar("CmdType", bound=Command) 

125 

126 

127@t.overload 

128def command( 

129 __func: t.Callable[..., t.Any], 

130) -> Command: 

131 ... 

132 

133 

134@t.overload 

135def command( 

136 name: t.Optional[str] = None, 

137 **attrs: t.Any, 

138) -> t.Callable[..., Command]: 

139 ... 

140 

141 

142@t.overload 

143def command( 

144 name: t.Optional[str] = None, 

145 cls: t.Type[CmdType] = ..., 

146 **attrs: t.Any, 

147) -> t.Callable[..., CmdType]: 

148 ... 

149 

150 

151def command( 

152 name: t.Union[str, t.Callable[..., t.Any], None] = None, 

153 cls: t.Optional[t.Type[Command]] = None, 

154 **attrs: t.Any, 

155) -> t.Union[Command, t.Callable[..., Command]]: 

156 r"""Creates a new :class:`Command` and uses the decorated function as 

157 callback. This will also automatically attach all decorated 

158 :func:`option`\s and :func:`argument`\s as parameters to the command. 

159 

160 The name of the command defaults to the name of the function with 

161 underscores replaced by dashes. If you want to change that, you can 

162 pass the intended name as the first argument. 

163 

164 All keyword arguments are forwarded to the underlying command class. 

165 For the ``params`` argument, any decorated params are appended to 

166 the end of the list. 

167 

168 Once decorated the function turns into a :class:`Command` instance 

169 that can be invoked as a command line utility or be attached to a 

170 command :class:`Group`. 

171 

172 :param name: the name of the command. This defaults to the function 

173 name with underscores replaced by dashes. 

174 :param cls: the command class to instantiate. This defaults to 

175 :class:`Command`. 

176 

177 .. versionchanged:: 8.1 

178 This decorator can be applied without parentheses. 

179 

180 .. versionchanged:: 8.1 

181 The ``params`` argument can be used. Decorated params are 

182 appended to the end of the list. 

183 """ 

184 

185 func: t.Optional[t.Callable[..., t.Any]] = None 

186 

187 if callable(name): 

188 func = name 

189 name = None 

190 assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." 

191 assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." 

192 

193 if cls is None: 

194 cls = Command 

195 

196 def decorator(f: t.Callable[..., t.Any]) -> Command: 

197 if isinstance(f, Command): 

198 raise TypeError("Attempted to convert a callback into a command twice.") 

199 

200 attr_params = attrs.pop("params", None) 

201 params = attr_params if attr_params is not None else [] 

202 

203 try: 

204 decorator_params = f.__click_params__ # type: ignore 

205 except AttributeError: 

206 pass 

207 else: 

208 del f.__click_params__ # type: ignore 

209 params.extend(reversed(decorator_params)) 

210 

211 if attrs.get("help") is None: 

212 attrs["help"] = f.__doc__ 

213 

214 cmd = cls( # type: ignore[misc] 

215 name=name or f.__name__.lower().replace("_", "-"), # type: ignore[arg-type] 

216 callback=f, 

217 params=params, 

218 **attrs, 

219 ) 

220 cmd.__doc__ = f.__doc__ 

221 return cmd 

222 

223 if func is not None: 

224 return decorator(func) 

225 

226 return decorator 

227 

228 

229@t.overload 

230def group( 

231 __func: t.Callable[..., t.Any], 

232) -> Group: 

233 ... 

234 

235 

236@t.overload 

237def group( 

238 name: t.Optional[str] = None, 

239 **attrs: t.Any, 

240) -> t.Callable[[F], Group]: 

241 ... 

242 

243 

244def group( 

245 name: t.Union[str, t.Callable[..., t.Any], None] = None, **attrs: t.Any 

246) -> t.Union[Group, t.Callable[[F], Group]]: 

247 """Creates a new :class:`Group` with a function as callback. This 

248 works otherwise the same as :func:`command` just that the `cls` 

249 parameter is set to :class:`Group`. 

250 

251 .. versionchanged:: 8.1 

252 This decorator can be applied without parentheses. 

253 """ 

254 if attrs.get("cls") is None: 

255 attrs["cls"] = Group 

256 

257 if callable(name): 

258 grp: t.Callable[[F], Group] = t.cast(Group, command(**attrs)) 

259 return grp(name) 

260 

261 return t.cast(Group, command(name, **attrs)) 

262 

263 

264def _param_memo(f: FC, param: Parameter) -> None: 

265 if isinstance(f, Command): 

266 f.params.append(param) 

267 else: 

268 if not hasattr(f, "__click_params__"): 

269 f.__click_params__ = [] # type: ignore 

270 

271 f.__click_params__.append(param) # type: ignore 

272 

273 

274def argument(*param_decls: str, **attrs: t.Any) -> t.Callable[[FC], FC]: 

275 """Attaches an argument to the command. All positional arguments are 

276 passed as parameter declarations to :class:`Argument`; all keyword 

277 arguments are forwarded unchanged (except ``cls``). 

278 This is equivalent to creating an :class:`Argument` instance manually 

279 and attaching it to the :attr:`Command.params` list. 

280 

281 :param cls: the argument class to instantiate. This defaults to 

282 :class:`Argument`. 

283 """ 

284 

285 def decorator(f: FC) -> FC: 

286 ArgumentClass = attrs.pop("cls", None) or Argument 

287 _param_memo(f, ArgumentClass(param_decls, **attrs)) 

288 return f 

289 

290 return decorator 

291 

292 

293def option(*param_decls: str, **attrs: t.Any) -> t.Callable[[FC], FC]: 

294 """Attaches an option to the command. All positional arguments are 

295 passed as parameter declarations to :class:`Option`; all keyword 

296 arguments are forwarded unchanged (except ``cls``). 

297 This is equivalent to creating an :class:`Option` instance manually 

298 and attaching it to the :attr:`Command.params` list. 

299 

300 :param cls: the option class to instantiate. This defaults to 

301 :class:`Option`. 

302 """ 

303 

304 def decorator(f: FC) -> FC: 

305 # Issue 926, copy attrs, so pre-defined options can re-use the same cls= 

306 option_attrs = attrs.copy() 

307 OptionClass = option_attrs.pop("cls", None) or Option 

308 _param_memo(f, OptionClass(param_decls, **option_attrs)) 

309 return f 

310 

311 return decorator 

312 

313 

314def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: 

315 """Add a ``--yes`` option which shows a prompt before continuing if 

316 not passed. If the prompt is declined, the program will exit. 

317 

318 :param param_decls: One or more option names. Defaults to the single 

319 value ``"--yes"``. 

320 :param kwargs: Extra arguments are passed to :func:`option`. 

321 """ 

322 

323 def callback(ctx: Context, param: Parameter, value: bool) -> None: 

324 if not value: 

325 ctx.abort() 

326 

327 if not param_decls: 

328 param_decls = ("--yes",) 

329 

330 kwargs.setdefault("is_flag", True) 

331 kwargs.setdefault("callback", callback) 

332 kwargs.setdefault("expose_value", False) 

333 kwargs.setdefault("prompt", "Do you want to continue?") 

334 kwargs.setdefault("help", "Confirm the action without prompting.") 

335 return option(*param_decls, **kwargs) 

336 

337 

338def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: 

339 """Add a ``--password`` option which prompts for a password, hiding 

340 input and asking to enter the value again for confirmation. 

341 

342 :param param_decls: One or more option names. Defaults to the single 

343 value ``"--password"``. 

344 :param kwargs: Extra arguments are passed to :func:`option`. 

345 """ 

346 if not param_decls: 

347 param_decls = ("--password",) 

348 

349 kwargs.setdefault("prompt", True) 

350 kwargs.setdefault("confirmation_prompt", True) 

351 kwargs.setdefault("hide_input", True) 

352 return option(*param_decls, **kwargs) 

353 

354 

355def version_option( 

356 version: t.Optional[str] = None, 

357 *param_decls: str, 

358 package_name: t.Optional[str] = None, 

359 prog_name: t.Optional[str] = None, 

360 message: t.Optional[str] = None, 

361 **kwargs: t.Any, 

362) -> t.Callable[[FC], FC]: 

363 """Add a ``--version`` option which immediately prints the version 

364 number and exits the program. 

365 

366 If ``version`` is not provided, Click will try to detect it using 

367 :func:`importlib.metadata.version` to get the version for the 

368 ``package_name``. On Python < 3.8, the ``importlib_metadata`` 

369 backport must be installed. 

370 

371 If ``package_name`` is not provided, Click will try to detect it by 

372 inspecting the stack frames. This will be used to detect the 

373 version, so it must match the name of the installed package. 

374 

375 :param version: The version number to show. If not provided, Click 

376 will try to detect it. 

377 :param param_decls: One or more option names. Defaults to the single 

378 value ``"--version"``. 

379 :param package_name: The package name to detect the version from. If 

380 not provided, Click will try to detect it. 

381 :param prog_name: The name of the CLI to show in the message. If not 

382 provided, it will be detected from the command. 

383 :param message: The message to show. The values ``%(prog)s``, 

384 ``%(package)s``, and ``%(version)s`` are available. Defaults to 

385 ``"%(prog)s, version %(version)s"``. 

386 :param kwargs: Extra arguments are passed to :func:`option`. 

387 :raise RuntimeError: ``version`` could not be detected. 

388 

389 .. versionchanged:: 8.0 

390 Add the ``package_name`` parameter, and the ``%(package)s`` 

391 value for messages. 

392 

393 .. versionchanged:: 8.0 

394 Use :mod:`importlib.metadata` instead of ``pkg_resources``. The 

395 version is detected based on the package name, not the entry 

396 point name. The Python package name must match the installed 

397 package name, or be passed with ``package_name=``. 

398 """ 

399 if message is None: 

400 message = _("%(prog)s, version %(version)s") 

401 

402 if version is None and package_name is None: 

403 frame = inspect.currentframe() 

404 f_back = frame.f_back if frame is not None else None 

405 f_globals = f_back.f_globals if f_back is not None else None 

406 # break reference cycle 

407 # https://docs.python.org/3/library/inspect.html#the-interpreter-stack 

408 del frame 

409 

410 if f_globals is not None: 

411 package_name = f_globals.get("__name__") 

412 

413 if package_name == "__main__": 

414 package_name = f_globals.get("__package__") 

415 

416 if package_name: 

417 package_name = package_name.partition(".")[0] 

418 

419 def callback(ctx: Context, param: Parameter, value: bool) -> None: 

420 if not value or ctx.resilient_parsing: 

421 return 

422 

423 nonlocal prog_name 

424 nonlocal version 

425 

426 if prog_name is None: 

427 prog_name = ctx.find_root().info_name 

428 

429 if version is None and package_name is not None: 

430 metadata: t.Optional[types.ModuleType] 

431 

432 try: 

433 from importlib import metadata # type: ignore 

434 except ImportError: 

435 # Python < 3.8 

436 import importlib_metadata as metadata # type: ignore 

437 

438 try: 

439 version = metadata.version(package_name) # type: ignore 

440 except metadata.PackageNotFoundError: # type: ignore 

441 raise RuntimeError( 

442 f"{package_name!r} is not installed. Try passing" 

443 " 'package_name' instead." 

444 ) from None 

445 

446 if version is None: 

447 raise RuntimeError( 

448 f"Could not determine the version for {package_name!r} automatically." 

449 ) 

450 

451 echo( 

452 t.cast(str, message) 

453 % {"prog": prog_name, "package": package_name, "version": version}, 

454 color=ctx.color, 

455 ) 

456 ctx.exit() 

457 

458 if not param_decls: 

459 param_decls = ("--version",) 

460 

461 kwargs.setdefault("is_flag", True) 

462 kwargs.setdefault("expose_value", False) 

463 kwargs.setdefault("is_eager", True) 

464 kwargs.setdefault("help", _("Show the version and exit.")) 

465 kwargs["callback"] = callback 

466 return option(*param_decls, **kwargs) 

467 

468 

469def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: 

470 """Add a ``--help`` option which immediately prints the help page 

471 and exits the program. 

472 

473 This is usually unnecessary, as the ``--help`` option is added to 

474 each command automatically unless ``add_help_option=False`` is 

475 passed. 

476 

477 :param param_decls: One or more option names. Defaults to the single 

478 value ``"--help"``. 

479 :param kwargs: Extra arguments are passed to :func:`option`. 

480 """ 

481 

482 def callback(ctx: Context, param: Parameter, value: bool) -> None: 

483 if not value or ctx.resilient_parsing: 

484 return 

485 

486 echo(ctx.get_help(), color=ctx.color) 

487 ctx.exit() 

488 

489 if not param_decls: 

490 param_decls = ("--help",) 

491 

492 kwargs.setdefault("is_flag", True) 

493 kwargs.setdefault("expose_value", False) 

494 kwargs.setdefault("is_eager", True) 

495 kwargs.setdefault("help", _("Show this message and exit.")) 

496 kwargs["callback"] = callback 

497 return option(*param_decls, **kwargs)