Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pip/_internal/cli/cmdoptions.py: 65%

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

251 statements  

1""" 

2shared options and groups 

3 

4The principle here is to define options once, but *not* instantiate them 

5globally. One reason being that options with action='append' can carry state 

6between parses. pip parses general options twice internally, and shouldn't 

7pass on state. To be consistent, all options will follow this design. 

8""" 

9 

10# The following comment should be removed at some point in the future. 

11# mypy: strict-optional=False 

12from __future__ import annotations 

13 

14import logging 

15import os 

16import pathlib 

17import textwrap 

18from functools import partial 

19from optparse import SUPPRESS_HELP, Option, OptionGroup, OptionParser, Values 

20from textwrap import dedent 

21from typing import Any, Callable 

22 

23from pip._vendor.packaging.utils import canonicalize_name 

24 

25from pip._internal.cli.parser import ConfigOptionParser 

26from pip._internal.exceptions import CommandError 

27from pip._internal.locations import USER_CACHE_DIR, get_src_prefix 

28from pip._internal.models.format_control import FormatControl 

29from pip._internal.models.index import PyPI 

30from pip._internal.models.release_control import ReleaseControl 

31from pip._internal.models.target_python import TargetPython 

32from pip._internal.utils.datetime import parse_iso_datetime 

33from pip._internal.utils.hashes import STRONG_HASHES 

34from pip._internal.utils.misc import strtobool 

35 

36logger = logging.getLogger(__name__) 

37 

38 

39def raise_option_error(parser: OptionParser, option: Option, msg: str) -> None: 

40 """ 

41 Raise an option parsing error using parser.error(). 

42 

43 Args: 

44 parser: an OptionParser instance. 

45 option: an Option instance. 

46 msg: the error text. 

47 """ 

48 msg = f"{option} error: {msg}" 

49 msg = textwrap.fill(" ".join(msg.split())) 

50 parser.error(msg) 

51 

52 

53def make_option_group(group: dict[str, Any], parser: ConfigOptionParser) -> OptionGroup: 

54 """ 

55 Return an OptionGroup object 

56 group -- assumed to be dict with 'name' and 'options' keys 

57 parser -- an optparse Parser 

58 """ 

59 option_group = OptionGroup(parser, group["name"]) 

60 for option in group["options"]: 

61 option_group.add_option(option()) 

62 return option_group 

63 

64 

65def check_dist_restriction(options: Values, check_target: bool = False) -> None: 

66 """Function for determining if custom platform options are allowed. 

67 

68 :param options: The OptionParser options. 

69 :param check_target: Whether or not to check if --target is being used. 

70 """ 

71 dist_restriction_set = any( 

72 [ 

73 options.python_version, 

74 options.platforms, 

75 options.abis, 

76 options.implementation, 

77 ] 

78 ) 

79 

80 binary_only = FormatControl(set(), {":all:"}) 

81 sdist_dependencies_allowed = ( 

82 options.format_control != binary_only and not options.ignore_dependencies 

83 ) 

84 

85 # Installations or downloads using dist restrictions must not combine 

86 # source distributions and dist-specific wheels, as they are not 

87 # guaranteed to be locally compatible. 

88 if dist_restriction_set and sdist_dependencies_allowed: 

89 raise CommandError( 

90 "When restricting platform and interpreter constraints using " 

91 "--python-version, --platform, --abi, or --implementation, " 

92 "either --no-deps must be set, or --only-binary=:all: must be " 

93 "set and --no-binary must not be set (or must be set to " 

94 ":none:)." 

95 ) 

96 

97 if check_target: 

98 if not options.dry_run and dist_restriction_set and not options.target_dir: 

99 raise CommandError( 

100 "Can not use any platform or abi specific options unless " 

101 "installing via '--target' or using '--dry-run'" 

102 ) 

103 

104 

105def check_build_constraints(options: Values) -> None: 

106 """Function for validating build constraints options. 

107 

108 :param options: The OptionParser options. 

109 """ 

110 if hasattr(options, "build_constraints") and options.build_constraints: 

111 if not options.build_isolation: 

112 raise CommandError( 

113 "--build-constraint cannot be used with --no-build-isolation." 

114 ) 

115 

116 # Import here to avoid circular imports 

117 from pip._internal.network.session import PipSession 

118 from pip._internal.req.req_file import get_file_content 

119 

120 # Eagerly check build constraints file contents 

121 # is valid so that we don't fail in when trying 

122 # to check constraints in isolated build process 

123 with PipSession() as session: 

124 for constraint_file in options.build_constraints: 

125 get_file_content(constraint_file, session) 

126 

127 

128def _path_option_check(option: Option, opt: str, value: str) -> str: 

129 return os.path.expanduser(value) 

130 

131 

132def _package_name_option_check(option: Option, opt: str, value: str) -> str: 

133 return canonicalize_name(value) 

134 

135 

136class PipOption(Option): 

137 TYPES = Option.TYPES + ("path", "package_name") 

138 TYPE_CHECKER = Option.TYPE_CHECKER.copy() 

139 TYPE_CHECKER["package_name"] = _package_name_option_check 

140 TYPE_CHECKER["path"] = _path_option_check 

141 

142 

143########### 

144# options # 

145########### 

146 

147help_: Callable[..., Option] = partial( 

148 Option, 

149 "-h", 

150 "--help", 

151 dest="help", 

152 action="help", 

153 help="Show help.", 

154) 

155 

156debug_mode: Callable[..., Option] = partial( 

157 Option, 

158 "--debug", 

159 dest="debug_mode", 

160 action="store_true", 

161 default=False, 

162 help=( 

163 "Let unhandled exceptions propagate outside the main subroutine, " 

164 "instead of logging them to stderr." 

165 ), 

166) 

167 

168isolated_mode: Callable[..., Option] = partial( 

169 Option, 

170 "--isolated", 

171 dest="isolated_mode", 

172 action="store_true", 

173 default=False, 

174 help=( 

175 "Run pip in an isolated mode, ignoring environment variables and user " 

176 "configuration." 

177 ), 

178) 

179 

180require_virtualenv: Callable[..., Option] = partial( 

181 Option, 

182 "--require-virtualenv", 

183 "--require-venv", 

184 dest="require_venv", 

185 action="store_true", 

186 default=False, 

187 help=( 

188 "Allow pip to only run in a virtual environment; exit with an error otherwise." 

189 ), 

190) 

191 

192override_externally_managed: Callable[..., Option] = partial( 

193 Option, 

194 "--break-system-packages", 

195 dest="override_externally_managed", 

196 action="store_true", 

197 help="Allow pip to modify an EXTERNALLY-MANAGED Python installation", 

198) 

199 

200python: Callable[..., Option] = partial( 

201 Option, 

202 "--python", 

203 dest="python", 

204 help="Run pip with the specified Python interpreter.", 

205) 

206 

207verbose: Callable[..., Option] = partial( 

208 Option, 

209 "-v", 

210 "--verbose", 

211 dest="verbose", 

212 action="count", 

213 default=0, 

214 help="Give more output. Option is additive, and can be used up to 3 times.", 

215) 

216 

217no_color: Callable[..., Option] = partial( 

218 Option, 

219 "--no-color", 

220 dest="no_color", 

221 action="store_true", 

222 default=False, 

223 help="Suppress colored output.", 

224) 

225 

226version: Callable[..., Option] = partial( 

227 Option, 

228 "-V", 

229 "--version", 

230 dest="version", 

231 action="store_true", 

232 help="Show version and exit.", 

233) 

234 

235quiet: Callable[..., Option] = partial( 

236 Option, 

237 "-q", 

238 "--quiet", 

239 dest="quiet", 

240 action="count", 

241 default=0, 

242 help=( 

243 "Give less output. Option is additive, and can be used up to 3" 

244 " times (corresponding to WARNING, ERROR, and CRITICAL logging" 

245 " levels)." 

246 ), 

247) 

248 

249progress_bar: Callable[..., Option] = partial( 

250 Option, 

251 "--progress-bar", 

252 dest="progress_bar", 

253 type="choice", 

254 choices=["auto", "on", "off", "raw"], 

255 default="auto", 

256 help=( 

257 "Specify whether the progress bar should be used. In 'auto'" 

258 " mode, --quiet will suppress all progress bars." 

259 " [auto, on, off, raw] (default: auto)" 

260 ), 

261) 

262 

263log: Callable[..., Option] = partial( 

264 PipOption, 

265 "--log", 

266 "--log-file", 

267 "--local-log", 

268 dest="log", 

269 metavar="path", 

270 type="path", 

271 help="Path to a verbose appending log.", 

272) 

273 

274no_input: Callable[..., Option] = partial( 

275 Option, 

276 # Don't ask for input 

277 "--no-input", 

278 dest="no_input", 

279 action="store_true", 

280 default=False, 

281 help="Disable prompting for input.", 

282) 

283 

284keyring_provider: Callable[..., Option] = partial( 

285 Option, 

286 "--keyring-provider", 

287 dest="keyring_provider", 

288 choices=["auto", "disabled", "import", "subprocess"], 

289 default="auto", 

290 help=( 

291 "Enable the credential lookup via the keyring library if user input is allowed." 

292 " Specify which mechanism to use [auto, disabled, import, subprocess]." 

293 " (default: %default)" 

294 ), 

295) 

296 

297proxy: Callable[..., Option] = partial( 

298 Option, 

299 "--proxy", 

300 dest="proxy", 

301 type="str", 

302 default="", 

303 help="Specify a proxy in the form scheme://[user:passwd@]proxy.server:port.", 

304) 

305 

306retries: Callable[..., Option] = partial( 

307 Option, 

308 "--retries", 

309 dest="retries", 

310 type="int", 

311 default=5, 

312 help="Maximum attempts to establish a new HTTP connection. (default: %default)", 

313) 

314 

315resume_retries: Callable[..., Option] = partial( 

316 Option, 

317 "--resume-retries", 

318 dest="resume_retries", 

319 type="int", 

320 default=5, 

321 help="Maximum attempts to resume or restart an incomplete download. " 

322 "(default: %default)", 

323) 

324 

325timeout: Callable[..., Option] = partial( 

326 Option, 

327 "--timeout", 

328 "--default-timeout", 

329 metavar="sec", 

330 dest="timeout", 

331 type="float", 

332 default=15, 

333 help="Set the socket timeout (default %default seconds).", 

334) 

335 

336 

337def exists_action() -> Option: 

338 return Option( 

339 # Option when path already exist 

340 "--exists-action", 

341 dest="exists_action", 

342 type="choice", 

343 choices=["s", "i", "w", "b", "a"], 

344 default=[], 

345 action="append", 

346 metavar="action", 

347 help="Default action when a path already exists: " 

348 "(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort.", 

349 ) 

350 

351 

352cert: Callable[..., Option] = partial( 

353 PipOption, 

354 "--cert", 

355 dest="cert", 

356 type="path", 

357 metavar="path", 

358 help=( 

359 "Path to PEM-encoded CA certificate bundle. " 

360 "If provided, overrides the default. " 

361 "See 'SSL Certificate Verification' in pip documentation " 

362 "for more information." 

363 ), 

364) 

365 

366client_cert: Callable[..., Option] = partial( 

367 PipOption, 

368 "--client-cert", 

369 dest="client_cert", 

370 type="path", 

371 default=None, 

372 metavar="path", 

373 help="Path to SSL client certificate, a single file containing the " 

374 "private key and the certificate in PEM format.", 

375) 

376 

377index_url: Callable[..., Option] = partial( 

378 Option, 

379 "-i", 

380 "--index-url", 

381 "--pypi-url", 

382 dest="index_url", 

383 metavar="URL", 

384 default=PyPI.simple_url, 

385 help="Base URL of the Python Package Index (default %default). " 

386 "This should point to a repository compliant with PEP 503 " 

387 "(the simple repository API) or a local directory laid out " 

388 "in the same format.", 

389) 

390 

391 

392def extra_index_url() -> Option: 

393 return Option( 

394 "--extra-index-url", 

395 dest="extra_index_urls", 

396 metavar="URL", 

397 action="append", 

398 default=[], 

399 help="Extra URLs of package indexes to use in addition to " 

400 "--index-url. Should follow the same rules as " 

401 "--index-url.", 

402 ) 

403 

404 

405no_index: Callable[..., Option] = partial( 

406 Option, 

407 "--no-index", 

408 dest="no_index", 

409 action="store_true", 

410 default=False, 

411 help="Ignore package index (only looking at --find-links URLs instead).", 

412) 

413 

414 

415def find_links() -> Option: 

416 return Option( 

417 "-f", 

418 "--find-links", 

419 dest="find_links", 

420 action="append", 

421 default=[], 

422 metavar="url", 

423 help="If a URL or path to an html file, then parse for links to " 

424 "archives such as sdist (.tar.gz) or wheel (.whl) files. " 

425 "If a local path or file:// URL that's a directory, " 

426 "then look for archives in the directory listing. " 

427 "Links to VCS project URLs are not supported.", 

428 ) 

429 

430 

431def _handle_uploaded_prior_to( 

432 option: Option, opt: str, value: str, parser: OptionParser 

433) -> None: 

434 """ 

435 This is an optparse.Option callback for the --uploaded-prior-to option. 

436 

437 Parses an ISO 8601 datetime string. If no timezone is specified in the string, 

438 local timezone is used. 

439 

440 Note: This option only works with indexes that provide upload-time metadata 

441 as specified in the simple repository API: 

442 https://packaging.python.org/en/latest/specifications/simple-repository-api/ 

443 """ 

444 if value is None: 

445 return None 

446 

447 try: 

448 uploaded_prior_to = parse_iso_datetime(value) 

449 # Use local timezone if no offset is given in the ISO string. 

450 if uploaded_prior_to.tzinfo is None: 

451 uploaded_prior_to = uploaded_prior_to.astimezone() 

452 parser.values.uploaded_prior_to = uploaded_prior_to 

453 except ValueError as exc: 

454 msg = ( 

455 f"invalid value: {value!r}: {exc}. " 

456 f"Expected an ISO 8601 datetime string, " 

457 f"e.g '2023-01-01' or '2023-01-01T00:00:00Z'" 

458 ) 

459 raise_option_error(parser, option=option, msg=msg) 

460 

461 

462def uploaded_prior_to() -> Option: 

463 return Option( 

464 "--uploaded-prior-to", 

465 dest="uploaded_prior_to", 

466 metavar="datetime", 

467 action="callback", 

468 callback=_handle_uploaded_prior_to, 

469 type="str", 

470 help=( 

471 "Only consider packages uploaded prior to the given date time. " 

472 "Accepts ISO 8601 strings (e.g., '2023-01-01T00:00:00Z'). " 

473 "Uses local timezone if none specified. Only effective when " 

474 "installing from indexes that provide upload-time metadata." 

475 ), 

476 ) 

477 

478 

479def trusted_host() -> Option: 

480 return Option( 

481 "--trusted-host", 

482 dest="trusted_hosts", 

483 action="append", 

484 metavar="HOSTNAME", 

485 default=[], 

486 help="Mark this host or host:port pair as trusted, even though it " 

487 "does not have valid or any HTTPS.", 

488 ) 

489 

490 

491def constraints() -> Option: 

492 return Option( 

493 "-c", 

494 "--constraint", 

495 dest="constraints", 

496 action="append", 

497 default=[], 

498 metavar="file", 

499 help="Constrain versions using the given constraints file. " 

500 "This option can be used multiple times.", 

501 ) 

502 

503 

504def build_constraints() -> Option: 

505 return Option( 

506 "--build-constraint", 

507 dest="build_constraints", 

508 action="append", 

509 type="str", 

510 default=[], 

511 metavar="file", 

512 help=( 

513 "Constrain build dependencies using the given constraints file. " 

514 "This option can be used multiple times." 

515 ), 

516 ) 

517 

518 

519def requirements() -> Option: 

520 return Option( 

521 "-r", 

522 "--requirement", 

523 dest="requirements", 

524 action="append", 

525 default=[], 

526 metavar="file", 

527 help="Install from the given requirements file. " 

528 "This option can be used multiple times.", 

529 ) 

530 

531 

532def requirements_from_scripts() -> Option: 

533 return Option( 

534 "--requirements-from-script", 

535 action="append", 

536 default=[], 

537 dest="requirements_from_scripts", 

538 metavar="file", 

539 help="Install dependencies of the given script file" 

540 "as defined by PEP 723 inline metadata. ", 

541 ) 

542 

543 

544def editable() -> Option: 

545 return Option( 

546 "-e", 

547 "--editable", 

548 dest="editables", 

549 action="append", 

550 default=[], 

551 metavar="path/url", 

552 help=( 

553 "Install a project in editable mode (i.e. setuptools " 

554 '"develop mode") from a local project path or a VCS url.' 

555 ), 

556 ) 

557 

558 

559def _handle_src(option: Option, opt_str: str, value: str, parser: OptionParser) -> None: 

560 value = os.path.abspath(value) 

561 setattr(parser.values, option.dest, value) 

562 

563 

564src: Callable[..., Option] = partial( 

565 PipOption, 

566 "--src", 

567 "--source", 

568 "--source-dir", 

569 "--source-directory", 

570 dest="src_dir", 

571 type="path", 

572 metavar="dir", 

573 default=get_src_prefix(), 

574 action="callback", 

575 callback=_handle_src, 

576 help="Directory to check out editable projects into. " 

577 'The default in a virtualenv is "<venv path>/src". ' 

578 'The default for global installs is "<current dir>/src".', 

579) 

580 

581 

582def _get_format_control(values: Values, option: Option) -> Any: 

583 """Get a format_control object.""" 

584 return getattr(values, option.dest) 

585 

586 

587def _handle_no_binary( 

588 option: Option, opt_str: str, value: str, parser: OptionParser 

589) -> None: 

590 existing = _get_format_control(parser.values, option) 

591 FormatControl.handle_mutual_excludes( 

592 value, 

593 existing.no_binary, 

594 existing.only_binary, 

595 ) 

596 

597 

598def _handle_only_binary( 

599 option: Option, opt_str: str, value: str, parser: OptionParser 

600) -> None: 

601 existing = _get_format_control(parser.values, option) 

602 FormatControl.handle_mutual_excludes( 

603 value, 

604 existing.only_binary, 

605 existing.no_binary, 

606 ) 

607 

608 

609def no_binary() -> Option: 

610 format_control = FormatControl(set(), set()) 

611 return Option( 

612 "--no-binary", 

613 dest="format_control", 

614 action="callback", 

615 callback=_handle_no_binary, 

616 type="str", 

617 default=format_control, 

618 help="Do not use binary packages. Can be supplied multiple times, and " 

619 'each time adds to the existing value. Accepts either ":all:" to ' 

620 'disable all binary packages, ":none:" to empty the set (notice ' 

621 "the colons), or one or more package names with commas between " 

622 "them (no colons). Note that some packages are tricky to compile " 

623 "and may fail to install when this option is used on them.", 

624 ) 

625 

626 

627def only_binary() -> Option: 

628 format_control = FormatControl(set(), set()) 

629 return Option( 

630 "--only-binary", 

631 dest="format_control", 

632 action="callback", 

633 callback=_handle_only_binary, 

634 type="str", 

635 default=format_control, 

636 help="Do not use source packages. Can be supplied multiple times, and " 

637 'each time adds to the existing value. Accepts either ":all:" to ' 

638 'disable all source packages, ":none:" to empty the set, or one ' 

639 "or more package names with commas between them. Packages " 

640 "without binary distributions will fail to install when this " 

641 "option is used on them.", 

642 ) 

643 

644 

645def _get_release_control(values: Values, option: Option) -> Any: 

646 """Get a release_control object.""" 

647 return getattr(values, option.dest) 

648 

649 

650def _handle_all_releases( 

651 option: Option, opt_str: str, value: str, parser: OptionParser 

652) -> None: 

653 existing = _get_release_control(parser.values, option) 

654 existing.handle_mutual_excludes( 

655 value, 

656 existing.all_releases, 

657 existing.only_final, 

658 "all_releases", 

659 ) 

660 

661 

662def _handle_only_final( 

663 option: Option, opt_str: str, value: str, parser: OptionParser 

664) -> None: 

665 existing = _get_release_control(parser.values, option) 

666 existing.handle_mutual_excludes( 

667 value, 

668 existing.only_final, 

669 existing.all_releases, 

670 "only_final", 

671 ) 

672 

673 

674def all_releases() -> Option: 

675 release_control = ReleaseControl(set(), set()) 

676 return Option( 

677 "--all-releases", 

678 dest="release_control", 

679 action="callback", 

680 callback=_handle_all_releases, 

681 type="str", 

682 default=release_control, 

683 help="Allow all release types (including pre-releases) for a package. " 

684 "Can be supplied multiple times, and each time adds to the existing " 

685 'value. Accepts either ":all:" to allow pre-releases for all ' 

686 'packages, ":none:" to empty the set (notice the colons), or one or ' 

687 "more package names with commas between them (no colons). Cannot be " 

688 "used with --pre.", 

689 ) 

690 

691 

692def only_final() -> Option: 

693 release_control = ReleaseControl(set(), set()) 

694 return Option( 

695 "--only-final", 

696 dest="release_control", 

697 action="callback", 

698 callback=_handle_only_final, 

699 type="str", 

700 default=release_control, 

701 help="Only allow final releases (no pre-releases) for a package. Can be " 

702 "supplied multiple times, and each time adds to the existing value. " 

703 'Accepts either ":all:" to disable pre-releases for all packages, ' 

704 '":none:" to empty the set, or one or more package names with commas ' 

705 "between them. Cannot be used with --pre.", 

706 ) 

707 

708 

709def check_release_control_exclusive(options: Values) -> None: 

710 """ 

711 Raise an error if --pre is used with --all-releases or --only-final, 

712 and transform --pre into --all-releases :all: if used alone. 

713 """ 

714 if not hasattr(options, "pre") or not options.pre: 

715 return 

716 

717 release_control = options.release_control 

718 if release_control.all_releases or release_control.only_final: 

719 raise CommandError("--pre cannot be used with --all-releases or --only-final.") 

720 

721 # Transform --pre into --all-releases :all: 

722 release_control.all_releases.add(":all:") 

723 

724 

725platforms: Callable[..., Option] = partial( 

726 Option, 

727 "--platform", 

728 dest="platforms", 

729 metavar="platform", 

730 action="append", 

731 default=None, 

732 help=( 

733 "Only use wheels compatible with <platform>. Defaults to the " 

734 "platform of the running system. Use this option multiple times to " 

735 "specify multiple platforms supported by the target interpreter." 

736 ), 

737) 

738 

739 

740# This was made a separate function for unit-testing purposes. 

741def _convert_python_version(value: str) -> tuple[tuple[int, ...], str | None]: 

742 """ 

743 Convert a version string like "3", "37", or "3.7.3" into a tuple of ints. 

744 

745 :return: A 2-tuple (version_info, error_msg), where `error_msg` is 

746 non-None if and only if there was a parsing error. 

747 """ 

748 if not value: 

749 # The empty string is the same as not providing a value. 

750 return (None, None) 

751 

752 parts = value.split(".") 

753 if len(parts) > 3: 

754 return ((), "at most three version parts are allowed") 

755 

756 if len(parts) == 1: 

757 # Then we are in the case of "3" or "37". 

758 value = parts[0] 

759 if len(value) > 1: 

760 parts = [value[0], value[1:]] 

761 

762 try: 

763 version_info = tuple(int(part) for part in parts) 

764 except ValueError: 

765 return ((), "each version part must be an integer") 

766 

767 return (version_info, None) 

768 

769 

770def _handle_python_version( 

771 option: Option, opt_str: str, value: str, parser: OptionParser 

772) -> None: 

773 """ 

774 Handle a provided --python-version value. 

775 """ 

776 version_info, error_msg = _convert_python_version(value) 

777 if error_msg is not None: 

778 msg = f"invalid --python-version value: {value!r}: {error_msg}" 

779 raise_option_error(parser, option=option, msg=msg) 

780 

781 parser.values.python_version = version_info 

782 

783 

784python_version: Callable[..., Option] = partial( 

785 Option, 

786 "--python-version", 

787 dest="python_version", 

788 metavar="python_version", 

789 action="callback", 

790 callback=_handle_python_version, 

791 type="str", 

792 default=None, 

793 help=dedent( 

794 """\ 

795 The Python interpreter version to use for wheel and "Requires-Python" 

796 compatibility checks. Defaults to a version derived from the running 

797 interpreter. The version can be specified using up to three dot-separated 

798 integers (e.g. "3" for 3.0.0, "3.7" for 3.7.0, or "3.7.3"). A major-minor 

799 version can also be given as a string without dots (e.g. "37" for 3.7.0). 

800 """ 

801 ), 

802) 

803 

804 

805implementation: Callable[..., Option] = partial( 

806 Option, 

807 "--implementation", 

808 dest="implementation", 

809 metavar="implementation", 

810 default=None, 

811 help=( 

812 "Only use wheels compatible with Python " 

813 "implementation <implementation>, e.g. 'pp', 'jy', 'cp', " 

814 " or 'ip'. If not specified, then the current " 

815 "interpreter implementation is used. Use 'py' to force " 

816 "implementation-agnostic wheels." 

817 ), 

818) 

819 

820 

821abis: Callable[..., Option] = partial( 

822 Option, 

823 "--abi", 

824 dest="abis", 

825 metavar="abi", 

826 action="append", 

827 default=None, 

828 help=( 

829 "Only use wheels compatible with Python abi <abi>, e.g. 'pypy_41'. " 

830 "If not specified, then the current interpreter abi tag is used. " 

831 "Use this option multiple times to specify multiple abis supported " 

832 "by the target interpreter. Generally you will need to specify " 

833 "--implementation, --platform, and --python-version when using this " 

834 "option." 

835 ), 

836) 

837 

838 

839def add_target_python_options(cmd_opts: OptionGroup) -> None: 

840 cmd_opts.add_option(platforms()) 

841 cmd_opts.add_option(python_version()) 

842 cmd_opts.add_option(implementation()) 

843 cmd_opts.add_option(abis()) 

844 

845 

846def make_target_python(options: Values) -> TargetPython: 

847 target_python = TargetPython( 

848 platforms=options.platforms, 

849 py_version_info=options.python_version, 

850 abis=options.abis, 

851 implementation=options.implementation, 

852 ) 

853 

854 return target_python 

855 

856 

857def prefer_binary() -> Option: 

858 return Option( 

859 "--prefer-binary", 

860 dest="prefer_binary", 

861 action="store_true", 

862 default=False, 

863 help=( 

864 "Prefer binary packages over source packages, even if the " 

865 "source packages are newer." 

866 ), 

867 ) 

868 

869 

870cache_dir: Callable[..., Option] = partial( 

871 PipOption, 

872 "--cache-dir", 

873 dest="cache_dir", 

874 default=USER_CACHE_DIR, 

875 metavar="dir", 

876 type="path", 

877 help="Store the cache data in <dir>.", 

878) 

879 

880 

881def _handle_no_cache_dir( 

882 option: Option, opt: str, value: str, parser: OptionParser 

883) -> None: 

884 """ 

885 Process a value provided for the --no-cache-dir option. 

886 

887 This is an optparse.Option callback for the --no-cache-dir option. 

888 """ 

889 # The value argument will be None if --no-cache-dir is passed via the 

890 # command-line, since the option doesn't accept arguments. However, 

891 # the value can be non-None if the option is triggered e.g. by an 

892 # environment variable, like PIP_NO_CACHE_DIR=true. 

893 if value is not None: 

894 # Then parse the string value to get argument error-checking. 

895 try: 

896 strtobool(value) 

897 except ValueError as exc: 

898 raise_option_error(parser, option=option, msg=str(exc)) 

899 

900 # Originally, setting PIP_NO_CACHE_DIR to a value that strtobool() 

901 # converted to 0 (like "false" or "no") caused cache_dir to be disabled 

902 # rather than enabled (logic would say the latter). Thus, we disable 

903 # the cache directory not just on values that parse to True, but (for 

904 # backwards compatibility reasons) also on values that parse to False. 

905 # In other words, always set it to False if the option is provided in 

906 # some (valid) form. 

907 parser.values.cache_dir = False 

908 

909 

910no_cache: Callable[..., Option] = partial( 

911 Option, 

912 "--no-cache-dir", 

913 dest="cache_dir", 

914 action="callback", 

915 callback=_handle_no_cache_dir, 

916 help="Disable the cache.", 

917) 

918 

919no_deps: Callable[..., Option] = partial( 

920 Option, 

921 "--no-deps", 

922 "--no-dependencies", 

923 dest="ignore_dependencies", 

924 action="store_true", 

925 default=False, 

926 help="Don't install package dependencies.", 

927) 

928 

929 

930def _handle_dependency_group( 

931 option: Option, opt: str, value: str, parser: OptionParser 

932) -> None: 

933 """ 

934 Process a value provided for the --group option. 

935 

936 Splits on the rightmost ":", and validates that the path (if present) ends 

937 in `pyproject.toml`. Defaults the path to `pyproject.toml` when one is not given. 

938 

939 `:` cannot appear in dependency group names, so this is a safe and simple parse. 

940 

941 This is an optparse.Option callback for the dependency_groups option. 

942 """ 

943 path, sep, groupname = value.rpartition(":") 

944 if not sep: 

945 path = "pyproject.toml" 

946 else: 

947 # check for 'pyproject.toml' filenames using pathlib 

948 if pathlib.PurePath(path).name != "pyproject.toml": 

949 msg = "group paths use 'pyproject.toml' filenames" 

950 raise_option_error(parser, option=option, msg=msg) 

951 

952 parser.values.dependency_groups.append((path, groupname)) 

953 

954 

955dependency_groups: Callable[..., Option] = partial( 

956 Option, 

957 "--group", 

958 dest="dependency_groups", 

959 default=[], 

960 type=str, 

961 action="callback", 

962 callback=_handle_dependency_group, 

963 metavar="[path:]group", 

964 help='Install a named dependency-group from a "pyproject.toml" file. ' 

965 'If a path is given, the name of the file must be "pyproject.toml". ' 

966 'Defaults to using "pyproject.toml" in the current directory.', 

967) 

968 

969ignore_requires_python: Callable[..., Option] = partial( 

970 Option, 

971 "--ignore-requires-python", 

972 dest="ignore_requires_python", 

973 action="store_true", 

974 help="Ignore the Requires-Python information.", 

975) 

976 

977 

978no_build_isolation: Callable[..., Option] = partial( 

979 Option, 

980 "--no-build-isolation", 

981 dest="build_isolation", 

982 action="store_false", 

983 default=True, 

984 help="Disable isolation when building a modern source distribution. " 

985 "Build dependencies specified by PEP 518 must be already installed " 

986 "if this option is used.", 

987) 

988 

989check_build_deps: Callable[..., Option] = partial( 

990 Option, 

991 "--check-build-dependencies", 

992 dest="check_build_deps", 

993 action="store_true", 

994 default=False, 

995 help="Check the build dependencies.", 

996) 

997 

998 

999use_pep517: Any = partial( 

1000 Option, 

1001 "--use-pep517", 

1002 dest="use_pep517", 

1003 action="store_true", 

1004 default=True, 

1005 help=SUPPRESS_HELP, 

1006) 

1007 

1008 

1009def _handle_config_settings( 

1010 option: Option, opt_str: str, value: str, parser: OptionParser 

1011) -> None: 

1012 key, sep, val = value.partition("=") 

1013 if sep != "=": 

1014 parser.error(f"Arguments to {opt_str} must be of the form KEY=VAL") 

1015 dest = getattr(parser.values, option.dest) 

1016 if dest is None: 

1017 dest = {} 

1018 setattr(parser.values, option.dest, dest) 

1019 if key in dest: 

1020 if isinstance(dest[key], list): 

1021 dest[key].append(val) 

1022 else: 

1023 dest[key] = [dest[key], val] 

1024 else: 

1025 dest[key] = val 

1026 

1027 

1028config_settings: Callable[..., Option] = partial( 

1029 Option, 

1030 "-C", 

1031 "--config-settings", 

1032 dest="config_settings", 

1033 type=str, 

1034 action="callback", 

1035 callback=_handle_config_settings, 

1036 metavar="settings", 

1037 help="Configuration settings to be passed to the build backend. " 

1038 "Settings take the form KEY=VALUE. Use multiple --config-settings options " 

1039 "to pass multiple keys to the backend.", 

1040) 

1041 

1042no_clean: Callable[..., Option] = partial( 

1043 Option, 

1044 "--no-clean", 

1045 action="store_true", 

1046 default=False, 

1047 help="Don't clean up build directories.", 

1048) 

1049 

1050pre: Callable[..., Option] = partial( 

1051 Option, 

1052 "--pre", 

1053 action="store_true", 

1054 default=False, 

1055 help="Include pre-release and development versions. By default, " 

1056 "pip only finds stable versions.", 

1057) 

1058 

1059json: Callable[..., Option] = partial( 

1060 Option, 

1061 "--json", 

1062 action="store_true", 

1063 default=False, 

1064 help="Output data in a machine-readable JSON format.", 

1065) 

1066 

1067disable_pip_version_check: Callable[..., Option] = partial( 

1068 Option, 

1069 "--disable-pip-version-check", 

1070 dest="disable_pip_version_check", 

1071 action="store_true", 

1072 default=False, 

1073 help="Don't periodically check PyPI to determine whether a new version " 

1074 "of pip is available for download. Implied with --no-index.", 

1075) 

1076 

1077root_user_action: Callable[..., Option] = partial( 

1078 Option, 

1079 "--root-user-action", 

1080 dest="root_user_action", 

1081 default="warn", 

1082 choices=["warn", "ignore"], 

1083 help="Action if pip is run as a root user [warn, ignore] (default: warn)", 

1084) 

1085 

1086 

1087def _handle_merge_hash( 

1088 option: Option, opt_str: str, value: str, parser: OptionParser 

1089) -> None: 

1090 """Given a value spelled "algo:digest", append the digest to a list 

1091 pointed to in a dict by the algo name.""" 

1092 if not parser.values.hashes: 

1093 parser.values.hashes = {} 

1094 try: 

1095 algo, digest = value.split(":", 1) 

1096 except ValueError: 

1097 parser.error( 

1098 f"Arguments to {opt_str} must be a hash name " 

1099 "followed by a value, like --hash=sha256:" 

1100 "abcde..." 

1101 ) 

1102 if algo not in STRONG_HASHES: 

1103 parser.error( 

1104 "Allowed hash algorithms for {} are {}.".format( 

1105 opt_str, ", ".join(STRONG_HASHES) 

1106 ) 

1107 ) 

1108 parser.values.hashes.setdefault(algo, []).append(digest) 

1109 

1110 

1111hash: Callable[..., Option] = partial( 

1112 Option, 

1113 "--hash", 

1114 # Hash values eventually end up in InstallRequirement.hashes due to 

1115 # __dict__ copying in process_line(). 

1116 dest="hashes", 

1117 action="callback", 

1118 callback=_handle_merge_hash, 

1119 type="string", 

1120 help="Verify that the package's archive matches this " 

1121 "hash before installing. Example: --hash=sha256:abcdef...", 

1122) 

1123 

1124 

1125require_hashes: Callable[..., Option] = partial( 

1126 Option, 

1127 "--require-hashes", 

1128 dest="require_hashes", 

1129 action="store_true", 

1130 default=False, 

1131 help="Require a hash to check each requirement against, for " 

1132 "repeatable installs. This option is implied when any package in a " 

1133 "requirements file has a --hash option.", 

1134) 

1135 

1136 

1137list_path: Callable[..., Option] = partial( 

1138 PipOption, 

1139 "--path", 

1140 dest="path", 

1141 type="path", 

1142 action="append", 

1143 help="Restrict to the specified installation path for listing " 

1144 "packages (can be used multiple times).", 

1145) 

1146 

1147 

1148def check_list_path_option(options: Values) -> None: 

1149 if options.path and (options.user or options.local): 

1150 raise CommandError("Cannot combine '--path' with '--user' or '--local'") 

1151 

1152 

1153list_exclude: Callable[..., Option] = partial( 

1154 PipOption, 

1155 "--exclude", 

1156 dest="excludes", 

1157 action="append", 

1158 metavar="package", 

1159 type="package_name", 

1160 help="Exclude specified package from the output", 

1161) 

1162 

1163 

1164no_python_version_warning: Callable[..., Option] = partial( 

1165 Option, 

1166 "--no-python-version-warning", 

1167 dest="no_python_version_warning", 

1168 action="store_true", 

1169 default=False, 

1170 help=SUPPRESS_HELP, # No-op, a hold-over from the Python 2->3 transition. 

1171) 

1172 

1173 

1174# Features that are now always on. A warning is printed if they are used. 

1175ALWAYS_ENABLED_FEATURES = [ 

1176 "truststore", # always on since 24.2 

1177 "no-binary-enable-wheel-cache", # always on since 23.1 

1178] 

1179 

1180use_new_feature: Callable[..., Option] = partial( 

1181 Option, 

1182 "--use-feature", 

1183 dest="features_enabled", 

1184 metavar="feature", 

1185 action="append", 

1186 default=[], 

1187 choices=[ 

1188 "fast-deps", 

1189 "build-constraint", 

1190 "inprocess-build-deps", 

1191 ] 

1192 + ALWAYS_ENABLED_FEATURES, 

1193 help="Enable new functionality, that may be backward incompatible.", 

1194) 

1195 

1196use_deprecated_feature: Callable[..., Option] = partial( 

1197 Option, 

1198 "--use-deprecated", 

1199 dest="deprecated_features_enabled", 

1200 metavar="feature", 

1201 action="append", 

1202 default=[], 

1203 choices=[ 

1204 "legacy-resolver", 

1205 "legacy-certs", 

1206 ], 

1207 help=("Enable deprecated functionality, that will be removed in the future."), 

1208) 

1209 

1210########## 

1211# groups # 

1212########## 

1213 

1214general_group: dict[str, Any] = { 

1215 "name": "General Options", 

1216 "options": [ 

1217 help_, 

1218 debug_mode, 

1219 isolated_mode, 

1220 require_virtualenv, 

1221 python, 

1222 verbose, 

1223 version, 

1224 quiet, 

1225 log, 

1226 no_input, 

1227 keyring_provider, 

1228 proxy, 

1229 retries, 

1230 timeout, 

1231 exists_action, 

1232 trusted_host, 

1233 cert, 

1234 client_cert, 

1235 cache_dir, 

1236 no_cache, 

1237 disable_pip_version_check, 

1238 no_color, 

1239 no_python_version_warning, 

1240 use_new_feature, 

1241 use_deprecated_feature, 

1242 resume_retries, 

1243 ], 

1244} 

1245 

1246index_group: dict[str, Any] = { 

1247 "name": "Package Index Options", 

1248 "options": [ 

1249 index_url, 

1250 extra_index_url, 

1251 no_index, 

1252 find_links, 

1253 uploaded_prior_to, 

1254 ], 

1255} 

1256 

1257package_selection_group: dict[str, Any] = { 

1258 "name": "Package Selection Options", 

1259 "options": [ 

1260 pre, 

1261 all_releases, 

1262 only_final, 

1263 no_binary, 

1264 only_binary, 

1265 prefer_binary, 

1266 ], 

1267}