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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

430 statements  

1from __future__ import annotations 

2 

3import collections.abc as cabc 

4import enum 

5import os 

6import stat 

7import sys 

8import typing as t 

9from datetime import datetime 

10from gettext import gettext as _ 

11from gettext import ngettext 

12 

13from ._compat import _get_argv_encoding 

14from ._compat import open_stream 

15from .exceptions import BadParameter 

16from .utils import format_filename 

17from .utils import LazyFile 

18from .utils import safecall 

19 

20if t.TYPE_CHECKING: 

21 import typing_extensions as te 

22 

23 from .core import Context 

24 from .core import Parameter 

25 from .shell_completion import CompletionItem 

26 

27ParamTypeValue = t.TypeVar("ParamTypeValue") 

28 

29 

30class ParamType: 

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

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

33 

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

35 following: 

36 

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

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

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

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

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

42 type. 

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

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

45 input. 

46 """ 

47 

48 is_composite: t.ClassVar[bool] = False 

49 arity: t.ClassVar[int] = 1 

50 

51 #: the descriptive name of this type 

52 name: str 

53 

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

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

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

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

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

59 #: Windows). 

60 envvar_list_splitter: t.ClassVar[str | None] = None 

61 

62 def to_info_dict(self) -> dict[str, t.Any]: 

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

64 user-facing documentation. 

65 

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

67 CLI structure. 

68 

69 .. versionadded:: 8.0 

70 """ 

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

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

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

74 

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

76 if hasattr(self, "name"): 

77 name = self.name 

78 else: 

79 name = param_type 

80 

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

82 

83 def __call__( 

84 self, 

85 value: t.Any, 

86 param: Parameter | None = None, 

87 ctx: Context | None = None, 

88 ) -> t.Any: 

89 if value is not None: 

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

91 

92 def get_metavar(self, param: Parameter, ctx: Context) -> str | None: 

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

94 

95 def get_missing_message(self, param: Parameter, ctx: Context | None) -> str | None: 

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

97 parameter. 

98 

99 .. versionadded:: 2.0 

100 """ 

101 

102 def convert( 

103 self, value: t.Any, param: Parameter | None, ctx: Context | None 

104 ) -> t.Any: 

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

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

107 

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

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

110 other compatible types. 

111 

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

113 situations, such as when converting prompt input. 

114 

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

116 descriptive message. 

117 

118 :param value: The value to convert. 

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

120 its value. May be ``None``. 

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

122 be ``None``. 

123 """ 

124 return value 

125 

126 def split_envvar_value(self, rv: str) -> cabc.Sequence[str]: 

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

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

129 

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

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

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

133 """ 

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

135 

136 def fail( 

137 self, 

138 message: str, 

139 param: Parameter | None = None, 

140 ctx: Context | None = None, 

141 ) -> t.NoReturn: 

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

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

144 

145 def shell_complete( 

146 self, ctx: Context, param: Parameter, incomplete: str 

147 ) -> list[CompletionItem]: 

148 """Return a list of 

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

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

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

152 completions as well. 

153 

154 :param ctx: Invocation context for this command. 

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

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

157 

158 .. versionadded:: 8.0 

159 """ 

160 return [] 

161 

162 

163class CompositeParamType(ParamType): 

164 is_composite = True 

165 

166 @property 

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

168 raise NotImplementedError() 

169 

170 

171class FuncParamType(ParamType): 

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

173 self.name: str = func.__name__ 

174 self.func = func 

175 

176 def to_info_dict(self) -> dict[str, t.Any]: 

177 info_dict = super().to_info_dict() 

178 info_dict["func"] = self.func 

179 return info_dict 

180 

181 def convert( 

182 self, value: t.Any, param: Parameter | None, ctx: Context | None 

183 ) -> t.Any: 

184 try: 

185 return self.func(value) 

186 except ValueError: 

187 try: 

188 value = str(value) 

189 except UnicodeError: 

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

191 

192 self.fail(value, param, ctx) 

193 

194 

195class UnprocessedParamType(ParamType): 

196 name = "text" 

197 

198 def convert( 

199 self, value: t.Any, param: Parameter | None, ctx: Context | None 

200 ) -> t.Any: 

201 return value 

202 

203 def __repr__(self) -> str: 

204 return "UNPROCESSED" 

205 

206 

207class StringParamType(ParamType): 

208 name = "text" 

209 

210 def convert( 

211 self, value: t.Any, param: Parameter | None, ctx: Context | None 

212 ) -> t.Any: 

213 if isinstance(value, bytes): 

214 enc = _get_argv_encoding() 

215 try: 

216 value = value.decode(enc) 

217 except UnicodeError: 

218 fs_enc = sys.getfilesystemencoding() 

219 if fs_enc != enc: 

220 try: 

221 value = value.decode(fs_enc) 

222 except UnicodeError: 

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

224 else: 

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

226 return value 

227 return str(value) 

228 

229 def __repr__(self) -> str: 

230 return "STRING" 

231 

232 

233class Choice(ParamType, t.Generic[ParamTypeValue]): 

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

235 of supported values. 

236 

237 You may pass any iterable value which will be converted to a tuple 

238 and thus will only be iterated once. 

239 

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

241 See :meth:`normalize_choice` for more info on the mapping of strings 

242 to choices. See :ref:`choice-opts` for an example. 

243 

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

245 insensitive. Defaults to true. 

246 

247 .. versionchanged:: 8.2.0 

248 Non-``str`` ``choices`` are now supported. It can additionally be any 

249 iterable. Before you were not recommended to pass anything but a list or 

250 tuple. 

251 

252 .. versionadded:: 8.2.0 

253 Choice normalization can be overridden via :meth:`normalize_choice`. 

254 """ 

255 

256 name = "choice" 

257 

258 def __init__( 

259 self, choices: cabc.Iterable[ParamTypeValue], case_sensitive: bool = True 

260 ) -> None: 

261 self.choices: cabc.Sequence[ParamTypeValue] = tuple(choices) 

262 self.case_sensitive = case_sensitive 

263 

264 def to_info_dict(self) -> dict[str, t.Any]: 

265 info_dict = super().to_info_dict() 

266 info_dict["choices"] = self.choices 

267 info_dict["case_sensitive"] = self.case_sensitive 

268 return info_dict 

269 

270 def _normalized_mapping( 

271 self, ctx: Context | None = None 

272 ) -> cabc.Mapping[ParamTypeValue, str]: 

273 """ 

274 Returns mapping where keys are the original choices and the values are 

275 the normalized values that are accepted via the command line. 

276 

277 This is a simple wrapper around :meth:`normalize_choice`, use that 

278 instead which is supported. 

279 """ 

280 return { 

281 choice: self.normalize_choice( 

282 choice=choice, 

283 ctx=ctx, 

284 ) 

285 for choice in self.choices 

286 } 

287 

288 def normalize_choice(self, choice: ParamTypeValue, ctx: Context | None) -> str: 

289 """ 

290 Normalize a choice value, used to map a passed string to a choice. 

291 Each choice must have a unique normalized value. 

292 

293 By default uses :meth:`Context.token_normalize_func` and if not case 

294 sensitive, convert it to a casefolded value. 

295 

296 .. versionadded:: 8.2.0 

297 """ 

298 normed_value = choice.name if isinstance(choice, enum.Enum) else str(choice) 

299 

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

301 normed_value = ctx.token_normalize_func(normed_value) 

302 

303 if not self.case_sensitive: 

304 normed_value = normed_value.casefold() 

305 

306 return normed_value 

307 

308 def get_metavar(self, param: Parameter, ctx: Context) -> str | None: 

309 if param.param_type_name == "option" and not param.show_choices: # type: ignore 

310 choice_metavars = [ 

311 convert_type(type(choice)).name.upper() for choice in self.choices 

312 ] 

313 choices_str = "|".join([*dict.fromkeys(choice_metavars)]) 

314 else: 

315 choices_str = "|".join( 

316 [str(i) for i in self._normalized_mapping(ctx=ctx).values()] 

317 ) 

318 

319 # Use curly braces to indicate a required argument. 

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

321 return f"{{{choices_str}}}" 

322 

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

324 return f"[{choices_str}]" 

325 

326 def get_missing_message(self, param: Parameter, ctx: Context | None) -> str: 

327 """ 

328 Message shown when no choice is passed. 

329 

330 .. versionchanged:: 8.2.0 Added ``ctx`` argument. 

331 """ 

332 return _("Choose from:\n\t{choices}").format( 

333 choices=",\n\t".join(self._normalized_mapping(ctx=ctx).values()) 

334 ) 

335 

336 def convert( 

337 self, value: t.Any, param: Parameter | None, ctx: Context | None 

338 ) -> ParamTypeValue: 

339 """ 

340 For a given value from the parser, normalize it and find its 

341 matching normalized value in the list of choices. Then return the 

342 matched "original" choice. 

343 """ 

344 normed_value = self.normalize_choice(choice=value, ctx=ctx) 

345 normalized_mapping = self._normalized_mapping(ctx=ctx) 

346 

347 try: 

348 return next( 

349 original 

350 for original, normalized in normalized_mapping.items() 

351 if normalized == normed_value 

352 ) 

353 except StopIteration: 

354 self.fail( 

355 self.get_invalid_choice_message(value=value, ctx=ctx), 

356 param=param, 

357 ctx=ctx, 

358 ) 

359 

360 def get_invalid_choice_message(self, value: t.Any, ctx: Context | None) -> str: 

361 """Get the error message when the given choice is invalid. 

362 

363 :param value: The invalid value. 

364 

365 .. versionadded:: 8.2 

366 """ 

367 choices_str = ", ".join(map(repr, self._normalized_mapping(ctx=ctx).values())) 

368 return ngettext( 

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

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

371 len(self.choices), 

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

373 

374 def __repr__(self) -> str: 

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

376 

377 def shell_complete( 

378 self, ctx: Context, param: Parameter, incomplete: str 

379 ) -> list[CompletionItem]: 

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

381 

382 :param ctx: Invocation context for this command. 

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

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

385 

386 .. versionadded:: 8.0 

387 """ 

388 from click.shell_completion import CompletionItem 

389 

390 str_choices = map(str, self.choices) 

391 

392 if self.case_sensitive: 

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

394 else: 

395 incomplete = incomplete.lower() 

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

397 

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

399 

400 

401class DateTime(ParamType): 

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

403 

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

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

406 

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

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

409 

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

411 consequently defines the format strings which are allowed. 

412 

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

414 parses successfully is used. 

415 

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

417 which they should be tried. Defaults to 

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

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

420 """ 

421 

422 name = "datetime" 

423 

424 def __init__(self, formats: cabc.Sequence[str] | None = None): 

425 self.formats: cabc.Sequence[str] = formats or [ 

426 "%Y-%m-%d", 

427 "%Y-%m-%dT%H:%M:%S", 

428 "%Y-%m-%d %H:%M:%S", 

429 ] 

430 

431 def to_info_dict(self) -> dict[str, t.Any]: 

432 info_dict = super().to_info_dict() 

433 info_dict["formats"] = self.formats 

434 return info_dict 

435 

436 def get_metavar(self, param: Parameter, ctx: Context) -> str | None: 

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

438 

439 def _try_to_convert_date(self, value: t.Any, format: str) -> datetime | None: 

440 try: 

441 return datetime.strptime(value, format) 

442 except ValueError: 

443 return None 

444 

445 def convert( 

446 self, value: t.Any, param: Parameter | None, ctx: Context | None 

447 ) -> t.Any: 

448 if isinstance(value, datetime): 

449 return value 

450 

451 for format in self.formats: 

452 converted = self._try_to_convert_date(value, format) 

453 

454 if converted is not None: 

455 return converted 

456 

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

458 self.fail( 

459 ngettext( 

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

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

462 len(self.formats), 

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

464 param, 

465 ctx, 

466 ) 

467 

468 def __repr__(self) -> str: 

469 return "DateTime" 

470 

471 

472class _NumberParamTypeBase(ParamType): 

473 _number_class: t.ClassVar[type[t.Any]] 

474 

475 def convert( 

476 self, value: t.Any, param: Parameter | None, ctx: Context | None 

477 ) -> t.Any: 

478 try: 

479 return self._number_class(value) 

480 except ValueError: 

481 self.fail( 

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

483 value=value, number_type=self.name 

484 ), 

485 param, 

486 ctx, 

487 ) 

488 

489 

490class _NumberRangeBase(_NumberParamTypeBase): 

491 def __init__( 

492 self, 

493 min: float | None = None, 

494 max: float | None = None, 

495 min_open: bool = False, 

496 max_open: bool = False, 

497 clamp: bool = False, 

498 ) -> None: 

499 self.min = min 

500 self.max = max 

501 self.min_open = min_open 

502 self.max_open = max_open 

503 self.clamp = clamp 

504 

505 def to_info_dict(self) -> dict[str, t.Any]: 

506 info_dict = super().to_info_dict() 

507 info_dict.update( 

508 min=self.min, 

509 max=self.max, 

510 min_open=self.min_open, 

511 max_open=self.max_open, 

512 clamp=self.clamp, 

513 ) 

514 return info_dict 

515 

516 def convert( 

517 self, value: t.Any, param: Parameter | None, ctx: Context | None 

518 ) -> t.Any: 

519 import operator 

520 

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

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

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

524 )(rv, self.min) 

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

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

527 )(rv, self.max) 

528 

529 if self.clamp: 

530 if lt_min: 

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

532 

533 if gt_max: 

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

535 

536 if lt_min or gt_max: 

537 self.fail( 

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

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

540 ), 

541 param, 

542 ctx, 

543 ) 

544 

545 return rv 

546 

547 def _clamp(self, bound: float, dir: t.Literal[1, -1], open: bool) -> float: 

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

549 direction. 

550 

551 :param bound: The boundary value. 

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

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

554 """ 

555 raise NotImplementedError 

556 

557 def _describe_range(self) -> str: 

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

559 if self.min is None: 

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

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

562 

563 if self.max is None: 

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

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

566 

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

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

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

570 

571 def __repr__(self) -> str: 

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

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

574 

575 

576class IntParamType(_NumberParamTypeBase): 

577 name = "integer" 

578 _number_class = int 

579 

580 def __repr__(self) -> str: 

581 return "INT" 

582 

583 

584class IntRange(_NumberRangeBase, IntParamType): 

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

586 values. See :ref:`ranges`. 

587 

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

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

590 corresponding boundary is not included in the range. 

591 

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

593 boundary instead of failing. 

594 

595 .. versionchanged:: 8.0 

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

597 """ 

598 

599 name = "integer range" 

600 

601 def _clamp( # type: ignore 

602 self, bound: int, dir: t.Literal[1, -1], open: bool 

603 ) -> int: 

604 if not open: 

605 return bound 

606 

607 return bound + dir 

608 

609 

610class FloatParamType(_NumberParamTypeBase): 

611 name = "float" 

612 _number_class = float 

613 

614 def __repr__(self) -> str: 

615 return "FLOAT" 

616 

617 

618class FloatRange(_NumberRangeBase, FloatParamType): 

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

620 values. See :ref:`ranges`. 

621 

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

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

624 corresponding boundary is not included in the range. 

625 

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

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

628 boundary is marked ``open``. 

629 

630 .. versionchanged:: 8.0 

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

632 """ 

633 

634 name = "float range" 

635 

636 def __init__( 

637 self, 

638 min: float | None = None, 

639 max: float | None = None, 

640 min_open: bool = False, 

641 max_open: bool = False, 

642 clamp: bool = False, 

643 ) -> None: 

644 super().__init__( 

645 min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp 

646 ) 

647 

648 if (min_open or max_open) and clamp: 

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

650 

651 def _clamp(self, bound: float, dir: t.Literal[1, -1], open: bool) -> float: 

652 if not open: 

653 return bound 

654 

655 # Could use math.nextafter here, but clamping an 

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

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

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

659 

660 

661class BoolParamType(ParamType): 

662 name = "boolean" 

663 

664 def convert( 

665 self, value: t.Any, param: Parameter | None, ctx: Context | None 

666 ) -> t.Any: 

667 if value in {False, True}: 

668 return bool(value) 

669 

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

671 

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

673 return True 

674 

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

676 return False 

677 

678 self.fail( 

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

680 ) 

681 

682 def __repr__(self) -> str: 

683 return "BOOL" 

684 

685 

686class UUIDParameterType(ParamType): 

687 name = "uuid" 

688 

689 def convert( 

690 self, value: t.Any, param: Parameter | None, ctx: Context | None 

691 ) -> t.Any: 

692 import uuid 

693 

694 if isinstance(value, uuid.UUID): 

695 return value 

696 

697 value = value.strip() 

698 

699 try: 

700 return uuid.UUID(value) 

701 except ValueError: 

702 self.fail( 

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

704 ) 

705 

706 def __repr__(self) -> str: 

707 return "UUID" 

708 

709 

710class File(ParamType): 

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

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

713 finished working). 

714 

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

716 indicates stdin or stdout depending on the mode. 

717 

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

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

720 to force a specific encoding. 

721 

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

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

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

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

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

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

728 

729 Files can also be opened atomically in which case all writes go into a 

730 separate file in the same folder and upon completion the file will 

731 be moved over to the original location. This is useful if a file 

732 regularly read by other users is modified. 

733 

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

735 

736 .. versionchanged:: 2.0 

737 Added the ``atomic`` parameter. 

738 """ 

739 

740 name = "filename" 

741 envvar_list_splitter: t.ClassVar[str] = os.path.pathsep 

742 

743 def __init__( 

744 self, 

745 mode: str = "r", 

746 encoding: str | None = None, 

747 errors: str | None = "strict", 

748 lazy: bool | None = None, 

749 atomic: bool = False, 

750 ) -> None: 

751 self.mode = mode 

752 self.encoding = encoding 

753 self.errors = errors 

754 self.lazy = lazy 

755 self.atomic = atomic 

756 

757 def to_info_dict(self) -> dict[str, t.Any]: 

758 info_dict = super().to_info_dict() 

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

760 return info_dict 

761 

762 def resolve_lazy_flag(self, value: str | os.PathLike[str]) -> bool: 

763 if self.lazy is not None: 

764 return self.lazy 

765 if os.fspath(value) == "-": 

766 return False 

767 elif "w" in self.mode: 

768 return True 

769 return False 

770 

771 def convert( 

772 self, 

773 value: str | os.PathLike[str] | t.IO[t.Any], 

774 param: Parameter | None, 

775 ctx: Context | None, 

776 ) -> t.IO[t.Any]: 

777 if _is_file_like(value): 

778 return value 

779 

780 value = t.cast("str | os.PathLike[str]", value) 

781 

782 try: 

783 lazy = self.resolve_lazy_flag(value) 

784 

785 if lazy: 

786 lf = LazyFile( 

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

788 ) 

789 

790 if ctx is not None: 

791 ctx.call_on_close(lf.close_intelligently) 

792 

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

794 

795 f, should_close = open_stream( 

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

797 ) 

798 

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

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

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

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

803 # type is used with prompts. 

804 if ctx is not None: 

805 if should_close: 

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

807 else: 

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

809 

810 return f 

811 except OSError as e: 

812 self.fail(f"'{format_filename(value)}': {e.strerror}", param, ctx) 

813 

814 def shell_complete( 

815 self, ctx: Context, param: Parameter, incomplete: str 

816 ) -> list[CompletionItem]: 

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

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

819 

820 :param ctx: Invocation context for this command. 

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

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

823 

824 .. versionadded:: 8.0 

825 """ 

826 from click.shell_completion import CompletionItem 

827 

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

829 

830 

831def _is_file_like(value: t.Any) -> te.TypeGuard[t.IO[t.Any]]: 

832 return hasattr(value, "read") or hasattr(value, "write") 

833 

834 

835class Path(ParamType): 

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

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

838 enabled to validate the type of file and permissions. 

839 

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

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

842 exist, then all further checks are silently skipped. 

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

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

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

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

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

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

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

850 done by the shell only. 

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

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

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

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

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

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

857 

858 .. versionchanged:: 8.1 

859 Added the ``executable`` parameter. 

860 

861 .. versionchanged:: 8.0 

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

863 

864 .. versionchanged:: 6.0 

865 Added the ``allow_dash`` parameter. 

866 """ 

867 

868 envvar_list_splitter: t.ClassVar[str] = os.path.pathsep 

869 

870 def __init__( 

871 self, 

872 exists: bool = False, 

873 file_okay: bool = True, 

874 dir_okay: bool = True, 

875 writable: bool = False, 

876 readable: bool = True, 

877 resolve_path: bool = False, 

878 allow_dash: bool = False, 

879 path_type: type[t.Any] | None = None, 

880 executable: bool = False, 

881 ): 

882 self.exists = exists 

883 self.file_okay = file_okay 

884 self.dir_okay = dir_okay 

885 self.readable = readable 

886 self.writable = writable 

887 self.executable = executable 

888 self.resolve_path = resolve_path 

889 self.allow_dash = allow_dash 

890 self.type = path_type 

891 

892 if self.file_okay and not self.dir_okay: 

893 self.name: str = _("file") 

894 elif self.dir_okay and not self.file_okay: 

895 self.name = _("directory") 

896 else: 

897 self.name = _("path") 

898 

899 def to_info_dict(self) -> dict[str, t.Any]: 

900 info_dict = super().to_info_dict() 

901 info_dict.update( 

902 exists=self.exists, 

903 file_okay=self.file_okay, 

904 dir_okay=self.dir_okay, 

905 writable=self.writable, 

906 readable=self.readable, 

907 allow_dash=self.allow_dash, 

908 ) 

909 return info_dict 

910 

911 def coerce_path_result( 

912 self, value: str | os.PathLike[str] 

913 ) -> str | bytes | os.PathLike[str]: 

914 if self.type is not None and not isinstance(value, self.type): 

915 if self.type is str: 

916 return os.fsdecode(value) 

917 elif self.type is bytes: 

918 return os.fsencode(value) 

919 else: 

920 return t.cast("os.PathLike[str]", self.type(value)) 

921 

922 return value 

923 

924 def convert( 

925 self, 

926 value: str | os.PathLike[str], 

927 param: Parameter | None, 

928 ctx: Context | None, 

929 ) -> str | bytes | os.PathLike[str]: 

930 rv = value 

931 

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

933 

934 if not is_dash: 

935 if self.resolve_path: 

936 rv = os.path.realpath(rv) 

937 

938 try: 

939 st = os.stat(rv) 

940 except OSError: 

941 if not self.exists: 

942 return self.coerce_path_result(rv) 

943 self.fail( 

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

945 name=self.name.title(), filename=format_filename(value) 

946 ), 

947 param, 

948 ctx, 

949 ) 

950 

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

952 self.fail( 

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

954 name=self.name.title(), filename=format_filename(value) 

955 ), 

956 param, 

957 ctx, 

958 ) 

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

960 self.fail( 

961 _("{name} {filename!r} is a directory.").format( 

962 name=self.name.title(), filename=format_filename(value) 

963 ), 

964 param, 

965 ctx, 

966 ) 

967 

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

969 self.fail( 

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

971 name=self.name.title(), filename=format_filename(value) 

972 ), 

973 param, 

974 ctx, 

975 ) 

976 

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

978 self.fail( 

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

980 name=self.name.title(), filename=format_filename(value) 

981 ), 

982 param, 

983 ctx, 

984 ) 

985 

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

987 self.fail( 

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

989 name=self.name.title(), filename=format_filename(value) 

990 ), 

991 param, 

992 ctx, 

993 ) 

994 

995 return self.coerce_path_result(rv) 

996 

997 def shell_complete( 

998 self, ctx: Context, param: Parameter, incomplete: str 

999 ) -> list[CompletionItem]: 

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

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

1002 directories or any paths. 

1003 

1004 :param ctx: Invocation context for this command. 

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

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

1007 

1008 .. versionadded:: 8.0 

1009 """ 

1010 from click.shell_completion import CompletionItem 

1011 

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

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

1014 

1015 

1016class Tuple(CompositeParamType): 

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

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

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

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

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

1022 

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

1024 

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

1026 

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

1028 """ 

1029 

1030 def __init__(self, types: cabc.Sequence[type[t.Any] | ParamType]) -> None: 

1031 self.types: cabc.Sequence[ParamType] = [convert_type(ty) for ty in types] 

1032 

1033 def to_info_dict(self) -> dict[str, t.Any]: 

1034 info_dict = super().to_info_dict() 

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

1036 return info_dict 

1037 

1038 @property 

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

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

1041 

1042 @property 

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

1044 return len(self.types) 

1045 

1046 def convert( 

1047 self, value: t.Any, param: Parameter | None, ctx: Context | None 

1048 ) -> t.Any: 

1049 len_type = len(self.types) 

1050 len_value = len(value) 

1051 

1052 if len_value != len_type: 

1053 self.fail( 

1054 ngettext( 

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

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

1057 len_value, 

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

1059 param=param, 

1060 ctx=ctx, 

1061 ) 

1062 

1063 return tuple( 

1064 ty(x, param, ctx) for ty, x in zip(self.types, value, strict=False) 

1065 ) 

1066 

1067 

1068def convert_type(ty: t.Any | None, default: t.Any | None = None) -> ParamType: 

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

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

1071 value. 

1072 """ 

1073 guessed_type = False 

1074 

1075 if ty is None and default is not None: 

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

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

1078 # return STRING. 

1079 if default: 

1080 item = default[0] 

1081 

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

1083 # Can't call convert recursively because that would 

1084 # incorrectly unwind the tuple to a single type. 

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

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

1087 else: 

1088 ty = type(item) 

1089 else: 

1090 ty = type(default) 

1091 

1092 guessed_type = True 

1093 

1094 if isinstance(ty, tuple): 

1095 return Tuple(ty) 

1096 

1097 if isinstance(ty, ParamType): 

1098 return ty 

1099 

1100 if ty is str or ty is None: 

1101 return STRING 

1102 

1103 if ty is int: 

1104 return INT 

1105 

1106 if ty is float: 

1107 return FLOAT 

1108 

1109 if ty is bool: 

1110 return BOOL 

1111 

1112 if guessed_type: 

1113 return STRING 

1114 

1115 if __debug__: 

1116 try: 

1117 if issubclass(ty, ParamType): 

1118 raise AssertionError( 

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

1120 ) 

1121 except TypeError: 

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

1123 pass 

1124 

1125 return FuncParamType(ty) 

1126 

1127 

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

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

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

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

1132#: appear in bytes and unicode. 

1133#: 

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

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

1136#: it is is provided. 

1137#: 

1138#: .. versionadded:: 4.0 

1139UNPROCESSED = UnprocessedParamType() 

1140 

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

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

1143STRING = StringParamType() 

1144 

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

1146#: type. 

1147INT = IntParamType() 

1148 

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

1150#: ``float`` as type. 

1151FLOAT = FloatParamType() 

1152 

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

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

1155BOOL = BoolParamType() 

1156 

1157#: A UUID parameter. 

1158UUID = UUIDParameterType() 

1159 

1160 

1161class OptionHelpExtra(t.TypedDict, total=False): 

1162 envvars: tuple[str, ...] 

1163 default: str 

1164 range: str 

1165 required: str