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

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

207 statements  

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 

16if t.TYPE_CHECKING: 

17 import typing_extensions as te 

18 

19 P = te.ParamSpec("P") 

20 

21R = t.TypeVar("R") 

22T = t.TypeVar("T") 

23_AnyCallable = t.Callable[..., t.Any] 

24FC = t.TypeVar("FC", bound=t.Union[_AnyCallable, Command]) 

25 

26 

27def pass_context(f: "t.Callable[te.Concatenate[Context, P], R]") -> "t.Callable[P, R]": 

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

29 object as first argument. 

30 """ 

31 

32 def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": 

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

34 

35 return update_wrapper(new_func, f) 

36 

37 

38def pass_obj(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": 

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

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

41 represents the state of a nested system. 

42 """ 

43 

44 def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": 

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

46 

47 return update_wrapper(new_func, f) 

48 

49 

50def make_pass_decorator( 

51 object_type: t.Type[T], ensure: bool = False 

52) -> t.Callable[["t.Callable[te.Concatenate[T, P], R]"], "t.Callable[P, R]"]: 

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

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

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

56 :func:`object_type`. 

57 

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

59 

60 from functools import update_wrapper 

61 

62 def decorator(f): 

63 @pass_context 

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

65 obj = ctx.find_object(object_type) 

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

67 return update_wrapper(new_func, f) 

68 return decorator 

69 

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

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

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

73 """ 

74 

75 def decorator(f: "t.Callable[te.Concatenate[T, P], R]") -> "t.Callable[P, R]": 

76 def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": 

77 ctx = get_current_context() 

78 

79 obj: t.Optional[T] 

80 if ensure: 

81 obj = ctx.ensure_object(object_type) 

82 else: 

83 obj = ctx.find_object(object_type) 

84 

85 if obj is None: 

86 raise RuntimeError( 

87 "Managed to invoke callback without a context" 

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

89 " existing." 

90 ) 

91 

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

93 

94 return update_wrapper(new_func, f) 

95 

96 return decorator # type: ignore[return-value] 

97 

98 

99def pass_meta_key( 

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

101) -> "t.Callable[[t.Callable[te.Concatenate[t.Any, P], R]], t.Callable[P, R]]": 

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

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

104 function. 

105 

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

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

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

109 key from Context.meta". 

110 

111 .. versionadded:: 8.0 

112 """ 

113 

114 def decorator(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": 

115 def new_func(*args: "P.args", **kwargs: "P.kwargs") -> R: 

116 ctx = get_current_context() 

117 obj = ctx.meta[key] 

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

119 

120 return update_wrapper(new_func, f) 

121 

122 if doc_description is None: 

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

124 

125 decorator.__doc__ = ( 

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

127 " to the decorated function." 

128 ) 

129 return decorator # type: ignore[return-value] 

130 

131 

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

133 

134 

135# variant: no call, directly as decorator for a function. 

136@t.overload 

137def command(name: _AnyCallable) -> Command: 

138 ... 

139 

140 

141# variant: with positional name and with positional or keyword cls argument: 

142# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...) 

143@t.overload 

144def command( 

145 name: t.Optional[str], 

146 cls: t.Type[CmdType], 

147 **attrs: t.Any, 

148) -> t.Callable[[_AnyCallable], CmdType]: 

149 ... 

150 

151 

152# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...) 

153@t.overload 

154def command( 

155 name: None = None, 

156 *, 

157 cls: t.Type[CmdType], 

158 **attrs: t.Any, 

159) -> t.Callable[[_AnyCallable], CmdType]: 

160 ... 

161 

162 

163# variant: with optional string name, no cls argument provided. 

164@t.overload 

165def command( 

166 name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any 

167) -> t.Callable[[_AnyCallable], Command]: 

168 ... 

169 

170 

171def command( 

172 name: t.Union[t.Optional[str], _AnyCallable] = None, 

173 cls: t.Optional[t.Type[CmdType]] = None, 

174 **attrs: t.Any, 

175) -> t.Union[Command, t.Callable[[_AnyCallable], t.Union[Command, CmdType]]]: 

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

177 callback. This will also automatically attach all decorated 

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

179 

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

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

182 pass the intended name as the first argument. 

183 

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

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

186 the end of the list. 

187 

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

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

190 command :class:`Group`. 

191 

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

193 name with underscores replaced by dashes. 

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

195 :class:`Command`. 

196 

197 .. versionchanged:: 8.1 

198 This decorator can be applied without parentheses. 

199 

200 .. versionchanged:: 8.1 

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

202 appended to the end of the list. 

203 """ 

204 

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

206 

207 if callable(name): 

208 func = name 

209 name = None 

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

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

212 

213 if cls is None: 

214 cls = t.cast(t.Type[CmdType], Command) 

215 

216 def decorator(f: _AnyCallable) -> CmdType: 

217 if isinstance(f, Command): 

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

219 

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

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

222 

223 try: 

224 decorator_params = f.__click_params__ # type: ignore 

225 except AttributeError: 

226 pass 

227 else: 

228 del f.__click_params__ # type: ignore 

229 params.extend(reversed(decorator_params)) 

230 

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

232 attrs["help"] = f.__doc__ 

233 

234 if t.TYPE_CHECKING: 

235 assert cls is not None 

236 assert not callable(name) 

237 

238 cmd = cls( 

239 name=name or f.__name__.lower().replace("_", "-"), 

240 callback=f, 

241 params=params, 

242 **attrs, 

243 ) 

244 cmd.__doc__ = f.__doc__ 

245 return cmd 

246 

247 if func is not None: 

248 return decorator(func) 

249 

250 return decorator 

251 

252 

253GrpType = t.TypeVar("GrpType", bound=Group) 

254 

255 

256# variant: no call, directly as decorator for a function. 

257@t.overload 

258def group(name: _AnyCallable) -> Group: 

259 ... 

260 

261 

262# variant: with positional name and with positional or keyword cls argument: 

263# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...) 

264@t.overload 

265def group( 

266 name: t.Optional[str], 

267 cls: t.Type[GrpType], 

268 **attrs: t.Any, 

269) -> t.Callable[[_AnyCallable], GrpType]: 

270 ... 

271 

272 

273# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...) 

274@t.overload 

275def group( 

276 name: None = None, 

277 *, 

278 cls: t.Type[GrpType], 

279 **attrs: t.Any, 

280) -> t.Callable[[_AnyCallable], GrpType]: 

281 ... 

282 

283 

284# variant: with optional string name, no cls argument provided. 

285@t.overload 

286def group( 

287 name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any 

288) -> t.Callable[[_AnyCallable], Group]: 

289 ... 

290 

291 

292def group( 

293 name: t.Union[str, _AnyCallable, None] = None, 

294 cls: t.Optional[t.Type[GrpType]] = None, 

295 **attrs: t.Any, 

296) -> t.Union[Group, t.Callable[[_AnyCallable], t.Union[Group, GrpType]]]: 

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

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

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

300 

301 .. versionchanged:: 8.1 

302 This decorator can be applied without parentheses. 

303 """ 

304 if cls is None: 

305 cls = t.cast(t.Type[GrpType], Group) 

306 

307 if callable(name): 

308 return command(cls=cls, **attrs)(name) 

309 

310 return command(name, cls, **attrs) 

311 

312 

313def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None: 

314 if isinstance(f, Command): 

315 f.params.append(param) 

316 else: 

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

318 f.__click_params__ = [] # type: ignore 

319 

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

321 

322 

323def argument( 

324 *param_decls: str, cls: t.Optional[t.Type[Argument]] = None, **attrs: t.Any 

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

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

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

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

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

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

331 

332 For the default argument class, refer to :class:`Argument` and 

333 :class:`Parameter` for descriptions of parameters. 

334 

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

336 :class:`Argument`. 

337 :param param_decls: Passed as positional arguments to the constructor of 

338 ``cls``. 

339 :param attrs: Passed as keyword arguments to the constructor of ``cls``. 

340 """ 

341 if cls is None: 

342 cls = Argument 

343 

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

345 _param_memo(f, cls(param_decls, **attrs)) 

346 return f 

347 

348 return decorator 

349 

350 

351def option( 

352 *param_decls: str, cls: t.Optional[t.Type[Option]] = None, **attrs: t.Any 

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

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

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

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

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

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

359 

360 For the default option class, refer to :class:`Option` and 

361 :class:`Parameter` for descriptions of parameters. 

362 

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

364 :class:`Option`. 

365 :param param_decls: Passed as positional arguments to the constructor of 

366 ``cls``. 

367 :param attrs: Passed as keyword arguments to the constructor of ``cls``. 

368 """ 

369 if cls is None: 

370 cls = Option 

371 

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

373 _param_memo(f, cls(param_decls, **attrs)) 

374 return f 

375 

376 return decorator 

377 

378 

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

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

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

382 

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

384 value ``"--yes"``. 

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

386 """ 

387 

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

389 if not value: 

390 ctx.abort() 

391 

392 if not param_decls: 

393 param_decls = ("--yes",) 

394 

395 kwargs.setdefault("is_flag", True) 

396 kwargs.setdefault("callback", callback) 

397 kwargs.setdefault("expose_value", False) 

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

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

400 return option(*param_decls, **kwargs) 

401 

402 

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

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

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

406 

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

408 value ``"--password"``. 

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

410 """ 

411 if not param_decls: 

412 param_decls = ("--password",) 

413 

414 kwargs.setdefault("prompt", True) 

415 kwargs.setdefault("confirmation_prompt", True) 

416 kwargs.setdefault("hide_input", True) 

417 return option(*param_decls, **kwargs) 

418 

419 

420def version_option( 

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

422 *param_decls: str, 

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

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

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

426 **kwargs: t.Any, 

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

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

429 number and exits the program. 

430 

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

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

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

434 backport must be installed. 

435 

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

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

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

439 

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

441 will try to detect it. 

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

443 value ``"--version"``. 

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

445 not provided, Click will try to detect it. 

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

447 provided, it will be detected from the command. 

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

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

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

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

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

453 

454 .. versionchanged:: 8.0 

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

456 value for messages. 

457 

458 .. versionchanged:: 8.0 

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

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

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

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

463 """ 

464 if message is None: 

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

466 

467 if version is None and package_name is None: 

468 frame = inspect.currentframe() 

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

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

471 # break reference cycle 

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

473 del frame 

474 

475 if f_globals is not None: 

476 package_name = f_globals.get("__name__") 

477 

478 if package_name == "__main__": 

479 package_name = f_globals.get("__package__") 

480 

481 if package_name: 

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

483 

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

485 if not value or ctx.resilient_parsing: 

486 return 

487 

488 nonlocal prog_name 

489 nonlocal version 

490 

491 if prog_name is None: 

492 prog_name = ctx.find_root().info_name 

493 

494 if version is None and package_name is not None: 

495 metadata: t.Optional[types.ModuleType] 

496 

497 try: 

498 from importlib import metadata # type: ignore 

499 except ImportError: 

500 # Python < 3.8 

501 import importlib_metadata as metadata # type: ignore 

502 

503 try: 

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

505 except metadata.PackageNotFoundError: # type: ignore 

506 raise RuntimeError( 

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

508 " 'package_name' instead." 

509 ) from None 

510 

511 if version is None: 

512 raise RuntimeError( 

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

514 ) 

515 

516 echo( 

517 message % {"prog": prog_name, "package": package_name, "version": version}, 

518 color=ctx.color, 

519 ) 

520 ctx.exit() 

521 

522 if not param_decls: 

523 param_decls = ("--version",) 

524 

525 kwargs.setdefault("is_flag", True) 

526 kwargs.setdefault("expose_value", False) 

527 kwargs.setdefault("is_eager", True) 

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

529 kwargs["callback"] = callback 

530 return option(*param_decls, **kwargs) 

531 

532 

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

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

535 and exits the program. 

536 

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

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

539 passed. 

540 

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

542 value ``"--help"``. 

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

544 """ 

545 

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

547 if not value or ctx.resilient_parsing: 

548 return 

549 

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

551 ctx.exit() 

552 

553 if not param_decls: 

554 param_decls = ("--help",) 

555 

556 kwargs.setdefault("is_flag", True) 

557 kwargs.setdefault("expose_value", False) 

558 kwargs.setdefault("is_eager", True) 

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

560 kwargs["callback"] = callback 

561 return option(*param_decls, **kwargs)