Coverage for src/click/types.py: 30%

408 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-05-21 11:49 +0200

1import os 

2import stat 

3import typing as t 

4from datetime import datetime 

5from gettext import gettext as _ 

6from gettext import ngettext 

7 

8from ._compat import _get_argv_encoding 

9from ._compat import get_filesystem_encoding 

10from ._compat import open_stream 

11from .exceptions import BadParameter 

12from .utils import LazyFile 

13from .utils import safecall 

14 

15if t.TYPE_CHECKING: 15 ↛ 16line 15 didn't jump to line 16, because the condition on line 15 was never true

16 import typing_extensions as te 

17 from .core import Context 

18 from .core import Parameter 

19 from .shell_completion import CompletionItem 

20 

21 

22class ParamType: 

23 """Represents the type of a parameter. Validates and converts values 

24 from the command line or Python into the correct type. 

25 

26 To implement a custom type, subclass and implement at least the 

27 following: 

28 

29 - The :attr:`name` class attribute must be set. 

30 - Calling an instance of the type with ``None`` must return 

31 ``None``. This is already implemented by default. 

32 - :meth:`convert` must convert string values to the correct type. 

33 - :meth:`convert` must accept values that are already the correct 

34 type. 

35 - It must be able to convert a value if the ``ctx`` and ``param`` 

36 arguments are ``None``. This can occur when converting prompt 

37 input. 

38 """ 

39 

40 is_composite: t.ClassVar[bool] = False 

41 arity: t.ClassVar[int] = 1 

42 

43 #: the descriptive name of this type 

44 name: str 

45 

46 #: if a list of this type is expected and the value is pulled from a 

47 #: string environment variable, this is what splits it up. `None` 

48 #: means any whitespace. For all parameters the general rule is that 

49 #: whitespace splits them up. The exception are paths and files which 

50 #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on 

51 #: Windows). 

52 envvar_list_splitter: t.ClassVar[t.Optional[str]] = None 

53 

54 def to_info_dict(self) -> t.Dict[str, t.Any]: 

55 """Gather information that could be useful for a tool generating 

56 user-facing documentation. 

57 

58 Use :meth:`click.Context.to_info_dict` to traverse the entire 

59 CLI structure. 

60 

61 .. versionadded:: 8.0 

62 """ 

63 # The class name without the "ParamType" suffix. 

64 param_type = type(self).__name__.partition("ParamType")[0] 

65 param_type = param_type.partition("ParameterType")[0] 

66 

67 # Custom subclasses might not remember to set a name. 

68 if hasattr(self, "name"): 

69 name = self.name 

70 else: 

71 name = param_type 

72 

73 return {"param_type": param_type, "name": name} 

74 

75 def __call__( 

76 self, 

77 value: t.Any, 

78 param: t.Optional["Parameter"] = None, 

79 ctx: t.Optional["Context"] = None, 

80 ) -> t.Any: 

81 if value is not None: 

82 return self.convert(value, param, ctx) 

83 

84 def get_metavar(self, param: "Parameter") -> t.Optional[str]: 

85 """Returns the metavar default for this param if it provides one.""" 

86 

87 def get_missing_message(self, param: "Parameter") -> t.Optional[str]: 

88 """Optionally might return extra information about a missing 

89 parameter. 

90 

91 .. versionadded:: 2.0 

92 """ 

93 

94 def convert( 

95 self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] 

96 ) -> t.Any: 

97 """Convert the value to the correct type. This is not called if 

98 the value is ``None`` (the missing value). 

99 

100 This must accept string values from the command line, as well as 

101 values that are already the correct type. It may also convert 

102 other compatible types. 

103 

104 The ``param`` and ``ctx`` arguments may be ``None`` in certain 

105 situations, such as when converting prompt input. 

106 

107 If the value cannot be converted, call :meth:`fail` with a 

108 descriptive message. 

109 

110 :param value: The value to convert. 

111 :param param: The parameter that is using this type to convert 

112 its value. May be ``None``. 

113 :param ctx: The current context that arrived at this value. May 

114 be ``None``. 

115 """ 

116 return value 

117 

118 def split_envvar_value(self, rv: str) -> t.Sequence[str]: 

119 """Given a value from an environment variable this splits it up 

120 into small chunks depending on the defined envvar list splitter. 

121 

122 If the splitter is set to `None`, which means that whitespace splits, 

123 then leading and trailing whitespace is ignored. Otherwise, leading 

124 and trailing splitters usually lead to empty items being included. 

125 """ 

126 return (rv or "").split(self.envvar_list_splitter) 

127 

128 def fail( 

129 self, 

130 message: str, 

131 param: t.Optional["Parameter"] = None, 

132 ctx: t.Optional["Context"] = None, 

133 ) -> "t.NoReturn": 

134 """Helper method to fail with an invalid value message.""" 

135 raise BadParameter(message, ctx=ctx, param=param) 

136 

137 def shell_complete( 

138 self, ctx: "Context", param: "Parameter", incomplete: str 

139 ) -> t.List["CompletionItem"]: 

140 """Return a list of 

141 :class:`~click.shell_completion.CompletionItem` objects for the 

142 incomplete value. Most types do not provide completions, but 

143 some do, and this allows custom types to provide custom 

144 completions as well. 

145 

146 :param ctx: Invocation context for this command. 

147 :param param: The parameter that is requesting completion. 

148 :param incomplete: Value being completed. May be empty. 

149 

150 .. versionadded:: 8.0 

151 """ 

152 return [] 

153 

154 

155class CompositeParamType(ParamType): 

156 is_composite = True 

157 

158 @property 

159 def arity(self) -> int: # type: ignore 

160 raise NotImplementedError() 

161 

162 

163class FuncParamType(ParamType): 

164 def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: 

165 self.name = func.__name__ 

166 self.func = func 

167 

168 def to_info_dict(self) -> t.Dict[str, t.Any]: 

169 info_dict = super().to_info_dict() 

170 info_dict["func"] = self.func 

171 return info_dict 

172 

173 def convert( 

174 self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] 

175 ) -> t.Any: 

176 try: 

177 return self.func(value) 

178 except ValueError: 

179 try: 

180 value = str(value) 

181 except UnicodeError: 

182 value = value.decode("utf-8", "replace") 

183 

184 self.fail(value, param, ctx) 

185 

186 

187class UnprocessedParamType(ParamType): 

188 name = "text" 

189 

190 def convert( 

191 self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] 

192 ) -> t.Any: 

193 return value 

194 

195 def __repr__(self) -> str: 

196 return "UNPROCESSED" 

197 

198 

199class StringParamType(ParamType): 

200 name = "text" 

201 

202 def convert( 

203 self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] 

204 ) -> t.Any: 

205 if isinstance(value, bytes): 

206 enc = _get_argv_encoding() 

207 try: 

208 value = value.decode(enc) 

209 except UnicodeError: 

210 fs_enc = get_filesystem_encoding() 

211 if fs_enc != enc: 

212 try: 

213 value = value.decode(fs_enc) 

214 except UnicodeError: 

215 value = value.decode("utf-8", "replace") 

216 else: 

217 value = value.decode("utf-8", "replace") 

218 return value 

219 return str(value) 

220 

221 def __repr__(self) -> str: 

222 return "STRING" 

223 

224 

225class Choice(ParamType): 

226 """The choice type allows a value to be checked against a fixed set 

227 of supported values. All of these values have to be strings. 

228 

229 You should only pass a list or tuple of choices. Other iterables 

230 (like generators) may lead to surprising results. 

231 

232 The resulting value will always be one of the originally passed choices 

233 regardless of ``case_sensitive`` or any ``ctx.token_normalize_func`` 

234 being specified. 

235 

236 See :ref:`choice-opts` for an example. 

237 

238 :param case_sensitive: Set to false to make choices case 

239 insensitive. Defaults to true. 

240 """ 

241 

242 name = "choice" 

243 

244 def __init__(self, choices: t.Sequence[str], case_sensitive: bool = True) -> None: 

245 self.choices = choices 

246 self.case_sensitive = case_sensitive 

247 

248 def to_info_dict(self) -> t.Dict[str, t.Any]: 

249 info_dict = super().to_info_dict() 

250 info_dict["choices"] = self.choices 

251 info_dict["case_sensitive"] = self.case_sensitive 

252 return info_dict 

253 

254 def get_metavar(self, param: "Parameter") -> str: 

255 choices_str = "|".join(self.choices) 

256 

257 # Use curly braces to indicate a required argument. 

258 if param.required and param.param_type_name == "argument": 

259 return f"{{{choices_str}}}" 

260 

261 # Use square braces to indicate an option or optional argument. 

262 return f"[{choices_str}]" 

263 

264 def get_missing_message(self, param: "Parameter") -> str: 

265 return _("Choose from:\n\t{choices}").format(choices=",\n\t".join(self.choices)) 

266 

267 def convert( 

268 self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] 

269 ) -> t.Any: 

270 # Match through normalization and case sensitivity 

271 # first do token_normalize_func, then lowercase 

272 # preserve original `value` to produce an accurate message in 

273 # `self.fail` 

274 normed_value = value 

275 normed_choices = {choice: choice for choice in self.choices} 

276 

277 if ctx is not None and ctx.token_normalize_func is not None: 

278 normed_value = ctx.token_normalize_func(value) 

279 normed_choices = { 

280 ctx.token_normalize_func(normed_choice): original 

281 for normed_choice, original in normed_choices.items() 

282 } 

283 

284 if not self.case_sensitive: 

285 normed_value = normed_value.casefold() 

286 normed_choices = { 

287 normed_choice.casefold(): original 

288 for normed_choice, original in normed_choices.items() 

289 } 

290 

291 if normed_value in normed_choices: 

292 return normed_choices[normed_value] 

293 

294 choices_str = ", ".join(map(repr, self.choices)) 

295 self.fail( 

296 ngettext( 

297 "{value!r} is not {choice}.", 

298 "{value!r} is not one of {choices}.", 

299 len(self.choices), 

300 ).format(value=value, choice=choices_str, choices=choices_str), 

301 param, 

302 ctx, 

303 ) 

304 

305 def __repr__(self) -> str: 

306 return f"Choice({list(self.choices)})" 

307 

308 def shell_complete( 

309 self, ctx: "Context", param: "Parameter", incomplete: str 

310 ) -> t.List["CompletionItem"]: 

311 """Complete choices that start with the incomplete value. 

312 

313 :param ctx: Invocation context for this command. 

314 :param param: The parameter that is requesting completion. 

315 :param incomplete: Value being completed. May be empty. 

316 

317 .. versionadded:: 8.0 

318 """ 

319 from click.shell_completion import CompletionItem 

320 

321 str_choices = map(str, self.choices) 

322 

323 if self.case_sensitive: 

324 matched = (c for c in str_choices if c.startswith(incomplete)) 

325 else: 

326 incomplete = incomplete.lower() 

327 matched = (c for c in str_choices if c.lower().startswith(incomplete)) 

328 

329 return [CompletionItem(c) for c in matched] 

330 

331 

332class DateTime(ParamType): 

333 """The DateTime type converts date strings into `datetime` objects. 

334 

335 The format strings which are checked are configurable, but default to some 

336 common (non-timezone aware) ISO 8601 formats. 

337 

338 When specifying *DateTime* formats, you should only pass a list or a tuple. 

339 Other iterables, like generators, may lead to surprising results. 

340 

341 The format strings are processed using ``datetime.strptime``, and this 

342 consequently defines the format strings which are allowed. 

343 

344 Parsing is tried using each format, in order, and the first format which 

345 parses successfully is used. 

346 

347 :param formats: A list or tuple of date format strings, in the order in 

348 which they should be tried. Defaults to 

349 ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, 

350 ``'%Y-%m-%d %H:%M:%S'``. 

351 """ 

352 

353 name = "datetime" 

354 

355 def __init__(self, formats: t.Optional[t.Sequence[str]] = None): 

356 self.formats = formats or ["%Y-%m-%d", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"] 

357 

358 def to_info_dict(self) -> t.Dict[str, t.Any]: 

359 info_dict = super().to_info_dict() 

360 info_dict["formats"] = self.formats 

361 return info_dict 

362 

363 def get_metavar(self, param: "Parameter") -> str: 

364 return f"[{'|'.join(self.formats)}]" 

365 

366 def _try_to_convert_date(self, value: t.Any, format: str) -> t.Optional[datetime]: 

367 try: 

368 return datetime.strptime(value, format) 

369 except ValueError: 

370 return None 

371 

372 def convert( 

373 self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] 

374 ) -> t.Any: 

375 if isinstance(value, datetime): 

376 return value 

377 

378 for format in self.formats: 

379 converted = self._try_to_convert_date(value, format) 

380 

381 if converted is not None: 

382 return converted 

383 

384 formats_str = ", ".join(map(repr, self.formats)) 

385 self.fail( 

386 ngettext( 

387 "{value!r} does not match the format {format}.", 

388 "{value!r} does not match the formats {formats}.", 

389 len(self.formats), 

390 ).format(value=value, format=formats_str, formats=formats_str), 

391 param, 

392 ctx, 

393 ) 

394 

395 def __repr__(self) -> str: 

396 return "DateTime" 

397 

398 

399class _NumberParamTypeBase(ParamType): 

400 _number_class: t.ClassVar[t.Type[t.Any]] 

401 

402 def convert( 

403 self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] 

404 ) -> t.Any: 

405 try: 

406 return self._number_class(value) 

407 except ValueError: 

408 self.fail( 

409 _("{value!r} is not a valid {number_type}.").format( 

410 value=value, number_type=self.name 

411 ), 

412 param, 

413 ctx, 

414 ) 

415 

416 

417class _NumberRangeBase(_NumberParamTypeBase): 

418 def __init__( 

419 self, 

420 min: t.Optional[float] = None, 

421 max: t.Optional[float] = None, 

422 min_open: bool = False, 

423 max_open: bool = False, 

424 clamp: bool = False, 

425 ) -> None: 

426 self.min = min 

427 self.max = max 

428 self.min_open = min_open 

429 self.max_open = max_open 

430 self.clamp = clamp 

431 

432 def to_info_dict(self) -> t.Dict[str, t.Any]: 

433 info_dict = super().to_info_dict() 

434 info_dict.update( 

435 min=self.min, 

436 max=self.max, 

437 min_open=self.min_open, 

438 max_open=self.max_open, 

439 clamp=self.clamp, 

440 ) 

441 return info_dict 

442 

443 def convert( 

444 self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] 

445 ) -> t.Any: 

446 import operator 

447 

448 rv = super().convert(value, param, ctx) 

449 lt_min: bool = self.min is not None and ( 

450 operator.le if self.min_open else operator.lt 

451 )(rv, self.min) 

452 gt_max: bool = self.max is not None and ( 

453 operator.ge if self.max_open else operator.gt 

454 )(rv, self.max) 

455 

456 if self.clamp: 

457 if lt_min: 

458 return self._clamp(self.min, 1, self.min_open) # type: ignore 

459 

460 if gt_max: 

461 return self._clamp(self.max, -1, self.max_open) # type: ignore 

462 

463 if lt_min or gt_max: 

464 self.fail( 

465 _("{value} is not in the range {range}.").format( 

466 value=rv, range=self._describe_range() 

467 ), 

468 param, 

469 ctx, 

470 ) 

471 

472 return rv 

473 

474 def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: 

475 """Find the valid value to clamp to bound in the given 

476 direction. 

477 

478 :param bound: The boundary value. 

479 :param dir: 1 or -1 indicating the direction to move. 

480 :param open: If true, the range does not include the bound. 

481 """ 

482 raise NotImplementedError 

483 

484 def _describe_range(self) -> str: 

485 """Describe the range for use in help text.""" 

486 if self.min is None: 

487 op = "<" if self.max_open else "<=" 

488 return f"x{op}{self.max}" 

489 

490 if self.max is None: 

491 op = ">" if self.min_open else ">=" 

492 return f"x{op}{self.min}" 

493 

494 lop = "<" if self.min_open else "<=" 

495 rop = "<" if self.max_open else "<=" 

496 return f"{self.min}{lop}x{rop}{self.max}" 

497 

498 def __repr__(self) -> str: 

499 clamp = " clamped" if self.clamp else "" 

500 return f"<{type(self).__name__} {self._describe_range()}{clamp}>" 

501 

502 

503class IntParamType(_NumberParamTypeBase): 

504 name = "integer" 

505 _number_class = int 

506 

507 def __repr__(self) -> str: 

508 return "INT" 

509 

510 

511class IntRange(_NumberRangeBase, IntParamType): 

512 """Restrict an :data:`click.INT` value to a range of accepted 

513 values. See :ref:`ranges`. 

514 

515 If ``min`` or ``max`` are not passed, any value is accepted in that 

516 direction. If ``min_open`` or ``max_open`` are enabled, the 

517 corresponding boundary is not included in the range. 

518 

519 If ``clamp`` is enabled, a value outside the range is clamped to the 

520 boundary instead of failing. 

521 

522 .. versionchanged:: 8.0 

523 Added the ``min_open`` and ``max_open`` parameters. 

524 """ 

525 

526 name = "integer range" 

527 

528 def _clamp( # type: ignore 

529 self, bound: int, dir: "te.Literal[1, -1]", open: bool 

530 ) -> int: 

531 if not open: 

532 return bound 

533 

534 return bound + dir 

535 

536 

537class FloatParamType(_NumberParamTypeBase): 

538 name = "float" 

539 _number_class = float 

540 

541 def __repr__(self) -> str: 

542 return "FLOAT" 

543 

544 

545class FloatRange(_NumberRangeBase, FloatParamType): 

546 """Restrict a :data:`click.FLOAT` value to a range of accepted 

547 values. See :ref:`ranges`. 

548 

549 If ``min`` or ``max`` are not passed, any value is accepted in that 

550 direction. If ``min_open`` or ``max_open`` are enabled, the 

551 corresponding boundary is not included in the range. 

552 

553 If ``clamp`` is enabled, a value outside the range is clamped to the 

554 boundary instead of failing. This is not supported if either 

555 boundary is marked ``open``. 

556 

557 .. versionchanged:: 8.0 

558 Added the ``min_open`` and ``max_open`` parameters. 

559 """ 

560 

561 name = "float range" 

562 

563 def __init__( 

564 self, 

565 min: t.Optional[float] = None, 

566 max: t.Optional[float] = None, 

567 min_open: bool = False, 

568 max_open: bool = False, 

569 clamp: bool = False, 

570 ) -> None: 

571 super().__init__( 

572 min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp 

573 ) 

574 

575 if (min_open or max_open) and clamp: 575 ↛ 576line 575 didn't jump to line 576, because the condition on line 575 was never true

576 raise TypeError("Clamping is not supported for open bounds.") 

577 

578 def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: 

579 if not open: 

580 return bound 

581 

582 # Could use Python 3.9's math.nextafter here, but clamping an 

583 # open float range doesn't seem to be particularly useful. It's 

584 # left up to the user to write a callback to do it if needed. 

585 raise RuntimeError("Clamping is not supported for open bounds.") 

586 

587 

588class BoolParamType(ParamType): 

589 name = "boolean" 

590 

591 def convert( 

592 self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] 

593 ) -> t.Any: 

594 if value in {False, True}: 

595 return bool(value) 

596 

597 norm = value.strip().lower() 

598 

599 if norm in {"1", "true", "t", "yes", "y", "on"}: 

600 return True 

601 

602 if norm in {"0", "false", "f", "no", "n", "off"}: 

603 return False 

604 

605 self.fail( 

606 _("{value!r} is not a valid boolean.").format(value=value), param, ctx 

607 ) 

608 

609 def __repr__(self) -> str: 

610 return "BOOL" 

611 

612 

613class UUIDParameterType(ParamType): 

614 name = "uuid" 

615 

616 def convert( 

617 self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] 

618 ) -> t.Any: 

619 import uuid 

620 

621 if isinstance(value, uuid.UUID): 

622 return value 

623 

624 value = value.strip() 

625 

626 try: 

627 return uuid.UUID(value) 

628 except ValueError: 

629 self.fail( 

630 _("{value!r} is not a valid UUID.").format(value=value), param, ctx 

631 ) 

632 

633 def __repr__(self) -> str: 

634 return "UUID" 

635 

636 

637class File(ParamType): 

638 """Declares a parameter to be a file for reading or writing. The file 

639 is automatically closed once the context tears down (after the command 

640 finished working). 

641 

642 Files can be opened for reading or writing. The special value ``-`` 

643 indicates stdin or stdout depending on the mode. 

644 

645 By default, the file is opened for reading text data, but it can also be 

646 opened in binary mode or for writing. The encoding parameter can be used 

647 to force a specific encoding. 

648 

649 The `lazy` flag controls if the file should be opened immediately or upon 

650 first IO. The default is to be non-lazy for standard input and output 

651 streams as well as files opened for reading, `lazy` otherwise. When opening a 

652 file lazily for reading, it is still opened temporarily for validation, but 

653 will not be held open until first IO. lazy is mainly useful when opening 

654 for writing to avoid creating the file until it is needed. 

655 

656 Starting with Click 2.0, files can also be opened atomically in which 

657 case all writes go into a separate file in the same folder and upon 

658 completion the file will be moved over to the original location. This 

659 is useful if a file regularly read by other users is modified. 

660 

661 See :ref:`file-args` for more information. 

662 """ 

663 

664 name = "filename" 

665 envvar_list_splitter = os.path.pathsep 

666 

667 def __init__( 

668 self, 

669 mode: str = "r", 

670 encoding: t.Optional[str] = None, 

671 errors: t.Optional[str] = "strict", 

672 lazy: t.Optional[bool] = None, 

673 atomic: bool = False, 

674 ) -> None: 

675 self.mode = mode 

676 self.encoding = encoding 

677 self.errors = errors 

678 self.lazy = lazy 

679 self.atomic = atomic 

680 

681 def to_info_dict(self) -> t.Dict[str, t.Any]: 

682 info_dict = super().to_info_dict() 

683 info_dict.update(mode=self.mode, encoding=self.encoding) 

684 return info_dict 

685 

686 def resolve_lazy_flag(self, value: t.Any) -> bool: 

687 if self.lazy is not None: 

688 return self.lazy 

689 if value == "-": 

690 return False 

691 elif "w" in self.mode: 

692 return True 

693 return False 

694 

695 def convert( 

696 self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] 

697 ) -> t.Any: 

698 try: 

699 if hasattr(value, "read") or hasattr(value, "write"): 

700 return value 

701 

702 lazy = self.resolve_lazy_flag(value) 

703 

704 if lazy: 

705 lf = LazyFile( 

706 value, self.mode, self.encoding, self.errors, atomic=self.atomic 

707 ) 

708 

709 if ctx is not None: 

710 ctx.call_on_close(lf.close_intelligently) 

711 

712 return t.cast(t.IO[t.Any], lf) 

713 

714 f, should_close = open_stream( 

715 value, self.mode, self.encoding, self.errors, atomic=self.atomic 

716 ) 

717 

718 # If a context is provided, we automatically close the file 

719 # at the end of the context execution (or flush out). If a 

720 # context does not exist, it's the caller's responsibility to 

721 # properly close the file. This for instance happens when the 

722 # type is used with prompts. 

723 if ctx is not None: 

724 if should_close: 

725 ctx.call_on_close(safecall(f.close)) 

726 else: 

727 ctx.call_on_close(safecall(f.flush)) 

728 

729 return f 

730 except OSError as e: # noqa: B014 

731 self.fail(f"'{os.fsdecode(value)}': {e.strerror}", param, ctx) 

732 

733 def shell_complete( 

734 self, ctx: "Context", param: "Parameter", incomplete: str 

735 ) -> t.List["CompletionItem"]: 

736 """Return a special completion marker that tells the completion 

737 system to use the shell to provide file path completions. 

738 

739 :param ctx: Invocation context for this command. 

740 :param param: The parameter that is requesting completion. 

741 :param incomplete: Value being completed. May be empty. 

742 

743 .. versionadded:: 8.0 

744 """ 

745 from click.shell_completion import CompletionItem 

746 

747 return [CompletionItem(incomplete, type="file")] 

748 

749 

750class Path(ParamType): 

751 """The ``Path`` type is similar to the :class:`File` type, but 

752 returns the filename instead of an open file. Various checks can be 

753 enabled to validate the type of file and permissions. 

754 

755 :param exists: The file or directory needs to exist for the value to 

756 be valid. If this is not set to ``True``, and the file does not 

757 exist, then all further checks are silently skipped. 

758 :param file_okay: Allow a file as a value. 

759 :param dir_okay: Allow a directory as a value. 

760 :param readable: if true, a readable check is performed. 

761 :param writable: if true, a writable check is performed. 

762 :param executable: if true, an executable check is performed. 

763 :param resolve_path: Make the value absolute and resolve any 

764 symlinks. A ``~`` is not expanded, as this is supposed to be 

765 done by the shell only. 

766 :param allow_dash: Allow a single dash as a value, which indicates 

767 a standard stream (but does not open it). Use 

768 :func:`~click.open_file` to handle opening this value. 

769 :param path_type: Convert the incoming path value to this type. If 

770 ``None``, keep Python's default, which is ``str``. Useful to 

771 convert to :class:`pathlib.Path`. 

772 

773 .. versionchanged:: 8.1 

774 Added the ``executable`` parameter. 

775 

776 .. versionchanged:: 8.0 

777 Allow passing ``path_type=pathlib.Path``. 

778 

779 .. versionchanged:: 6.0 

780 Added the ``allow_dash`` parameter. 

781 """ 

782 

783 envvar_list_splitter = os.path.pathsep 

784 

785 def __init__( 

786 self, 

787 exists: bool = False, 

788 file_okay: bool = True, 

789 dir_okay: bool = True, 

790 writable: bool = False, 

791 readable: bool = True, 

792 resolve_path: bool = False, 

793 allow_dash: bool = False, 

794 path_type: t.Optional[t.Type[t.Any]] = None, 

795 executable: bool = False, 

796 ): 

797 self.exists = exists 

798 self.file_okay = file_okay 

799 self.dir_okay = dir_okay 

800 self.readable = readable 

801 self.writable = writable 

802 self.executable = executable 

803 self.resolve_path = resolve_path 

804 self.allow_dash = allow_dash 

805 self.type = path_type 

806 

807 if self.file_okay and not self.dir_okay: 807 ↛ 809line 807 didn't jump to line 809, because the condition on line 807 was never false

808 self.name = _("file") 

809 elif self.dir_okay and not self.file_okay: 

810 self.name = _("directory") 

811 else: 

812 self.name = _("path") 

813 

814 def to_info_dict(self) -> t.Dict[str, t.Any]: 

815 info_dict = super().to_info_dict() 

816 info_dict.update( 

817 exists=self.exists, 

818 file_okay=self.file_okay, 

819 dir_okay=self.dir_okay, 

820 writable=self.writable, 

821 readable=self.readable, 

822 allow_dash=self.allow_dash, 

823 ) 

824 return info_dict 

825 

826 def coerce_path_result(self, rv: t.Any) -> t.Any: 

827 if self.type is not None and not isinstance(rv, self.type): 

828 if self.type is str: 

829 rv = os.fsdecode(rv) 

830 elif self.type is bytes: 

831 rv = os.fsencode(rv) 

832 else: 

833 rv = self.type(rv) 

834 

835 return rv 

836 

837 def convert( 

838 self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] 

839 ) -> t.Any: 

840 rv = value 

841 

842 is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") 

843 

844 if not is_dash: 

845 if self.resolve_path: 

846 # os.path.realpath doesn't resolve symlinks on Windows 

847 # until Python 3.8. Use pathlib for now. 

848 import pathlib 

849 

850 rv = os.fsdecode(pathlib.Path(rv).resolve()) 

851 

852 try: 

853 st = os.stat(rv) 

854 except OSError: 

855 if not self.exists: 

856 return self.coerce_path_result(rv) 

857 self.fail( 

858 _("{name} {filename!r} does not exist.").format( 

859 name=self.name.title(), filename=os.fsdecode(value) 

860 ), 

861 param, 

862 ctx, 

863 ) 

864 

865 if not self.file_okay and stat.S_ISREG(st.st_mode): 

866 self.fail( 

867 _("{name} {filename!r} is a file.").format( 

868 name=self.name.title(), filename=os.fsdecode(value) 

869 ), 

870 param, 

871 ctx, 

872 ) 

873 if not self.dir_okay and stat.S_ISDIR(st.st_mode): 

874 self.fail( 

875 _("{name} '{filename}' is a directory.").format( 

876 name=self.name.title(), filename=os.fsdecode(value) 

877 ), 

878 param, 

879 ctx, 

880 ) 

881 

882 if self.readable and not os.access(rv, os.R_OK): 

883 self.fail( 

884 _("{name} {filename!r} is not readable.").format( 

885 name=self.name.title(), filename=os.fsdecode(value) 

886 ), 

887 param, 

888 ctx, 

889 ) 

890 

891 if self.writable and not os.access(rv, os.W_OK): 

892 self.fail( 

893 _("{name} {filename!r} is not writable.").format( 

894 name=self.name.title(), filename=os.fsdecode(value) 

895 ), 

896 param, 

897 ctx, 

898 ) 

899 

900 if self.executable and not os.access(value, os.X_OK): 

901 self.fail( 

902 _("{name} {filename!r} is not executable.").format( 

903 name=self.name.title(), filename=os.fsdecode(value) 

904 ), 

905 param, 

906 ctx, 

907 ) 

908 

909 return self.coerce_path_result(rv) 

910 

911 def shell_complete( 

912 self, ctx: "Context", param: "Parameter", incomplete: str 

913 ) -> t.List["CompletionItem"]: 

914 """Return a special completion marker that tells the completion 

915 system to use the shell to provide path completions for only 

916 directories or any paths. 

917 

918 :param ctx: Invocation context for this command. 

919 :param param: The parameter that is requesting completion. 

920 :param incomplete: Value being completed. May be empty. 

921 

922 .. versionadded:: 8.0 

923 """ 

924 from click.shell_completion import CompletionItem 

925 

926 type = "dir" if self.dir_okay and not self.file_okay else "file" 

927 return [CompletionItem(incomplete, type=type)] 

928 

929 

930class Tuple(CompositeParamType): 

931 """The default behavior of Click is to apply a type on a value directly. 

932 This works well in most cases, except for when `nargs` is set to a fixed 

933 count and different types should be used for different items. In this 

934 case the :class:`Tuple` type can be used. This type can only be used 

935 if `nargs` is set to a fixed number. 

936 

937 For more information see :ref:`tuple-type`. 

938 

939 This can be selected by using a Python tuple literal as a type. 

940 

941 :param types: a list of types that should be used for the tuple items. 

942 """ 

943 

944 def __init__(self, types: t.Sequence[t.Union[t.Type[t.Any], ParamType]]) -> None: 

945 self.types = [convert_type(ty) for ty in types] 945 ↛ exit,   945 ↛ exit2 missed branches: 1) line 945 didn't run the list comprehension on line 945, 2) line 945 didn't return from function '__init__'

946 

947 def to_info_dict(self) -> t.Dict[str, t.Any]: 

948 info_dict = super().to_info_dict() 

949 info_dict["types"] = [t.to_info_dict() for t in self.types] 

950 return info_dict 

951 

952 @property 

953 def name(self) -> str: # type: ignore 

954 return f"<{' '.join(ty.name for ty in self.types)}>" 

955 

956 @property 

957 def arity(self) -> int: # type: ignore 

958 return len(self.types) 

959 

960 def convert( 

961 self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] 

962 ) -> t.Any: 

963 len_type = len(self.types) 

964 len_value = len(value) 

965 

966 if len_value != len_type: 

967 self.fail( 

968 ngettext( 

969 "{len_type} values are required, but {len_value} was given.", 

970 "{len_type} values are required, but {len_value} were given.", 

971 len_value, 

972 ).format(len_type=len_type, len_value=len_value), 

973 param=param, 

974 ctx=ctx, 

975 ) 

976 

977 return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) 

978 

979 

980def convert_type(ty: t.Optional[t.Any], default: t.Optional[t.Any] = None) -> ParamType: 

981 """Find the most appropriate :class:`ParamType` for the given Python 

982 type. If the type isn't provided, it can be inferred from a default 

983 value. 

984 """ 

985 guessed_type = False 

986 

987 if ty is None and default is not None: 

988 if isinstance(default, (tuple, list)): 988 ↛ 991,   988 ↛ 10022 missed branches: 1) line 988 didn't jump to line 991, because the condition on line 988 was never true, 2) line 988 didn't jump to line 1002, because the condition on line 988 was never false

989 # If the default is empty, ty will remain None and will 

990 # return STRING. 

991 if default: 991 ↛ 992,   991 ↛ 10042 missed branches: 1) line 991 didn't jump to line 992, because the condition on line 991 was never true, 2) line 991 didn't jump to line 1004, because the condition on line 991 was never false

992 item = default[0] 

993 

994 # A tuple of tuples needs to detect the inner types. 

995 # Can't call convert recursively because that would 

996 # incorrectly unwind the tuple to a single type. 

997 if isinstance(item, (tuple, list)): 

998 ty = tuple(map(type, item)) 

999 else: 

1000 ty = type(item) 

1001 else: 

1002 ty = type(default) 

1003 

1004 guessed_type = True 

1005 

1006 if isinstance(ty, tuple): 

1007 return Tuple(ty) 

1008 

1009 if isinstance(ty, ParamType): 1009 ↛ 1010line 1009 didn't jump to line 1010, because the condition on line 1009 was never true

1010 return ty 

1011 

1012 if ty is str or ty is None: 

1013 return STRING 

1014 

1015 if ty is int: 

1016 return INT 

1017 

1018 if ty is float: 

1019 return FLOAT 

1020 

1021 if ty is bool: 1021 ↛ 1022line 1021 didn't jump to line 1022, because the condition on line 1021 was never true

1022 return BOOL 

1023 

1024 if guessed_type: 1024 ↛ 1028line 1024 didn't jump to line 1028, because the condition on line 1024 was never false

1025 return STRING 

1026 

1027 if __debug__: 

1028 try: 

1029 if issubclass(ty, ParamType): 

1030 raise AssertionError( 

1031 f"Attempted to use an uninstantiated parameter type ({ty})." 

1032 ) 

1033 except TypeError: 

1034 # ty is an instance (correct), so issubclass fails. 

1035 pass 

1036 

1037 return FuncParamType(ty) 

1038 

1039 

1040#: A dummy parameter type that just does nothing. From a user's 

1041#: perspective this appears to just be the same as `STRING` but 

1042#: internally no string conversion takes place if the input was bytes. 

1043#: This is usually useful when working with file paths as they can 

1044#: appear in bytes and unicode. 

1045#: 

1046#: For path related uses the :class:`Path` type is a better choice but 

1047#: there are situations where an unprocessed type is useful which is why 

1048#: it is is provided. 

1049#: 

1050#: .. versionadded:: 4.0 

1051UNPROCESSED = UnprocessedParamType() 

1052 

1053#: A unicode string parameter type which is the implicit default. This 

1054#: can also be selected by using ``str`` as type. 

1055STRING = StringParamType() 

1056 

1057#: An integer parameter. This can also be selected by using ``int`` as 

1058#: type. 

1059INT = IntParamType() 

1060 

1061#: A floating point value parameter. This can also be selected by using 

1062#: ``float`` as type. 

1063FLOAT = FloatParamType() 

1064 

1065#: A boolean parameter. This is the default for boolean flags. This can 

1066#: also be selected by using ``bool`` as a type. 

1067BOOL = BoolParamType() 

1068 

1069#: A UUID parameter. 

1070UUID = UUIDParameterType()