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

408 statements  

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

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] 

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: 

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 f: t.IO = t.cast( 

706 t.IO, 

707 LazyFile( 

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

709 ), 

710 ) 

711 

712 if ctx is not None: 

713 ctx.call_on_close(f.close_intelligently) # type: ignore 

714 

715 return f 

716 

717 f, should_close = open_stream( 

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

719 ) 

720 

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

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

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

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

725 # type is used with prompts. 

726 if ctx is not None: 

727 if should_close: 

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

729 else: 

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

731 

732 return f 

733 except OSError as e: # noqa: B014 

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

735 

736 def shell_complete( 

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

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

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

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

741 

742 :param ctx: Invocation context for this command. 

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

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

745 

746 .. versionadded:: 8.0 

747 """ 

748 from click.shell_completion import CompletionItem 

749 

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

751 

752 

753class Path(ParamType): 

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

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

756 enabled to validate the type of file and permissions. 

757 

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

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

760 exist, then all further checks are silently skipped. 

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

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

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

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

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

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

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

768 done by the shell only. 

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

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

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

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

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

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

775 

776 .. versionchanged:: 8.1 

777 Added the ``executable`` parameter. 

778 

779 .. versionchanged:: 8.0 

780 Allow passing ``type=pathlib.Path``. 

781 

782 .. versionchanged:: 6.0 

783 Added the ``allow_dash`` parameter. 

784 """ 

785 

786 envvar_list_splitter = os.path.pathsep 

787 

788 def __init__( 

789 self, 

790 exists: bool = False, 

791 file_okay: bool = True, 

792 dir_okay: bool = True, 

793 writable: bool = False, 

794 readable: bool = True, 

795 resolve_path: bool = False, 

796 allow_dash: bool = False, 

797 path_type: t.Optional[t.Type] = None, 

798 executable: bool = False, 

799 ): 

800 self.exists = exists 

801 self.file_okay = file_okay 

802 self.dir_okay = dir_okay 

803 self.readable = readable 

804 self.writable = writable 

805 self.executable = executable 

806 self.resolve_path = resolve_path 

807 self.allow_dash = allow_dash 

808 self.type = path_type 

809 

810 if self.file_okay and not self.dir_okay: 

811 self.name = _("file") 

812 elif self.dir_okay and not self.file_okay: 

813 self.name = _("directory") 

814 else: 

815 self.name = _("path") 

816 

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

818 info_dict = super().to_info_dict() 

819 info_dict.update( 

820 exists=self.exists, 

821 file_okay=self.file_okay, 

822 dir_okay=self.dir_okay, 

823 writable=self.writable, 

824 readable=self.readable, 

825 allow_dash=self.allow_dash, 

826 ) 

827 return info_dict 

828 

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

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

831 if self.type is str: 

832 rv = os.fsdecode(rv) 

833 elif self.type is bytes: 

834 rv = os.fsencode(rv) 

835 else: 

836 rv = self.type(rv) 

837 

838 return rv 

839 

840 def convert( 

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

842 ) -> t.Any: 

843 rv = value 

844 

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

846 

847 if not is_dash: 

848 if self.resolve_path: 

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

850 # until Python 3.8. Use pathlib for now. 

851 import pathlib 

852 

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

854 

855 try: 

856 st = os.stat(rv) 

857 except OSError: 

858 if not self.exists: 

859 return self.coerce_path_result(rv) 

860 self.fail( 

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

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

863 ), 

864 param, 

865 ctx, 

866 ) 

867 

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

869 self.fail( 

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

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

872 ), 

873 param, 

874 ctx, 

875 ) 

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

877 self.fail( 

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

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

880 ), 

881 param, 

882 ctx, 

883 ) 

884 

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

886 self.fail( 

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

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

889 ), 

890 param, 

891 ctx, 

892 ) 

893 

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

895 self.fail( 

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

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

898 ), 

899 param, 

900 ctx, 

901 ) 

902 

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

904 self.fail( 

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

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

907 ), 

908 param, 

909 ctx, 

910 ) 

911 

912 return self.coerce_path_result(rv) 

913 

914 def shell_complete( 

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

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

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

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

919 directories or any paths. 

920 

921 :param ctx: Invocation context for this command. 

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

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

924 

925 .. versionadded:: 8.0 

926 """ 

927 from click.shell_completion import CompletionItem 

928 

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

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

931 

932 

933class Tuple(CompositeParamType): 

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

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

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

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

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

939 

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

941 

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

943 

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

945 """ 

946 

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

948 self.types = [convert_type(ty) for ty in types] 

949 

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

951 info_dict = super().to_info_dict() 

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

953 return info_dict 

954 

955 @property 

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

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

958 

959 @property 

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

961 return len(self.types) 

962 

963 def convert( 

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

965 ) -> t.Any: 

966 len_type = len(self.types) 

967 len_value = len(value) 

968 

969 if len_value != len_type: 

970 self.fail( 

971 ngettext( 

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

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

974 len_value, 

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

976 param=param, 

977 ctx=ctx, 

978 ) 

979 

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

981 

982 

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

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

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

986 value. 

987 """ 

988 guessed_type = False 

989 

990 if ty is None and default is not None: 

991 if isinstance(default, (tuple, list)): 

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

993 # return STRING. 

994 if default: 

995 item = default[0] 

996 

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

998 # Can't call convert recursively because that would 

999 # incorrectly unwind the tuple to a single type. 

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

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

1002 else: 

1003 ty = type(item) 

1004 else: 

1005 ty = type(default) 

1006 

1007 guessed_type = True 

1008 

1009 if isinstance(ty, tuple): 

1010 return Tuple(ty) 

1011 

1012 if isinstance(ty, ParamType): 

1013 return ty 

1014 

1015 if ty is str or ty is None: 

1016 return STRING 

1017 

1018 if ty is int: 

1019 return INT 

1020 

1021 if ty is float: 

1022 return FLOAT 

1023 

1024 if ty is bool: 

1025 return BOOL 

1026 

1027 if guessed_type: 

1028 return STRING 

1029 

1030 if __debug__: 

1031 try: 

1032 if issubclass(ty, ParamType): 

1033 raise AssertionError( 

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

1035 ) 

1036 except TypeError: 

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

1038 pass 

1039 

1040 return FuncParamType(ty) 

1041 

1042 

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

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

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

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

1047#: appear in bytes and unicode. 

1048#: 

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

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

1051#: it is is provided. 

1052#: 

1053#: .. versionadded:: 4.0 

1054UNPROCESSED = UnprocessedParamType() 

1055 

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

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

1058STRING = StringParamType() 

1059 

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

1061#: type. 

1062INT = IntParamType() 

1063 

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

1065#: ``float`` as type. 

1066FLOAT = FloatParamType() 

1067 

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

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

1070BOOL = BoolParamType() 

1071 

1072#: A UUID parameter. 

1073UUID = UUIDParameterType()