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

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

263 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 re 

18import textwrap 

19from collections.abc import Callable 

20from datetime import datetime, timedelta, timezone 

21from functools import partial 

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

23from textwrap import dedent 

24from typing import Any 

25 

26from pip._vendor.packaging.utils import canonicalize_name 

27 

28from pip._internal.cli.parser import ConfigOptionParser 

29from pip._internal.exceptions import CommandError 

30from pip._internal.locations import USER_CACHE_DIR, get_src_prefix 

31from pip._internal.models.format_control import FormatControl 

32from pip._internal.models.index import PyPI 

33from pip._internal.models.release_control import ReleaseControl 

34from pip._internal.models.target_python import TargetPython 

35from pip._internal.utils import pylock as pylock_utils 

36from pip._internal.utils.datetime import parse_iso_datetime 

37from pip._internal.utils.hashes import STRONG_HASHES 

38from pip._internal.utils.misc import strtobool 

39 

40logger = logging.getLogger(__name__) 

41 

42 

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

44 """ 

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

46 

47 Args: 

48 parser: an OptionParser instance. 

49 option: an Option instance. 

50 msg: the error text. 

51 """ 

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

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

54 parser.error(msg) 

55 

56 

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

58 """ 

59 Return an OptionGroup object 

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

61 parser -- an optparse Parser 

62 """ 

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

64 for option in group["options"]: 

65 option_group.add_option(option()) 

66 return option_group 

67 

68 

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

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

71 

72 :param options: The OptionParser options. 

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

74 """ 

75 dist_restriction_set = any( 

76 [ 

77 options.python_version, 

78 options.platforms, 

79 options.abis, 

80 options.implementation, 

81 ] 

82 ) 

83 

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

85 sdist_dependencies_allowed = ( 

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

87 ) 

88 

89 # Installations or downloads using dist restrictions must not combine 

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

91 # guaranteed to be locally compatible. 

92 if dist_restriction_set and sdist_dependencies_allowed: 

93 raise CommandError( 

94 "When restricting platform and interpreter constraints using " 

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

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

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

98 ":none:)." 

99 ) 

100 

101 if check_target: 

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

103 raise CommandError( 

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

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

106 ) 

107 

108 for filename in options.requirements: 

109 if dist_restriction_set and pylock_utils.is_valid_pylock_filename(filename): 

110 raise CommandError( 

111 "Platform and interpreter constraints using " 

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

113 f"are not supported when selecting requirements from {filename!r}" 

114 ) 

115 

116 

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

118 """Function for validating build constraints options. 

119 

120 :param options: The OptionParser options. 

121 """ 

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

123 if not options.build_isolation: 

124 raise CommandError( 

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

126 ) 

127 

128 # Import here to avoid circular imports 

129 from pip._internal.network.session import PipSession 

130 from pip._internal.req.req_file import get_file_content 

131 

132 # Eagerly check build constraints file contents 

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

134 # to check constraints in isolated build process 

135 with PipSession() as session: 

136 for constraint_file in options.build_constraints: 

137 get_file_content(constraint_file, session) 

138 

139 

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

141 return os.path.expanduser(value) 

142 

143 

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

145 return canonicalize_name(value) 

146 

147 

148class PipOption(Option): 

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

150 TYPE_CHECKER = Option.TYPE_CHECKER.copy() 

151 TYPE_CHECKER["package_name"] = _package_name_option_check 

152 TYPE_CHECKER["path"] = _path_option_check 

153 

154 

155########### 

156# options # 

157########### 

158 

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

160 Option, 

161 "-h", 

162 "--help", 

163 dest="help", 

164 action="help", 

165 help="Show help.", 

166) 

167 

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

169 Option, 

170 "--debug", 

171 dest="debug_mode", 

172 action="store_true", 

173 default=False, 

174 help=( 

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

176 "instead of logging them to stderr." 

177 ), 

178) 

179 

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

181 Option, 

182 "--isolated", 

183 dest="isolated_mode", 

184 action="store_true", 

185 default=False, 

186 help=( 

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

188 "configuration." 

189 ), 

190) 

191 

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

193 Option, 

194 "--require-virtualenv", 

195 "--require-venv", 

196 dest="require_venv", 

197 action="store_true", 

198 default=False, 

199 help=( 

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

201 ), 

202) 

203 

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

205 Option, 

206 "--break-system-packages", 

207 dest="override_externally_managed", 

208 action="store_true", 

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

210) 

211 

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

213 Option, 

214 "--python", 

215 dest="python", 

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

217) 

218 

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

220 Option, 

221 "-v", 

222 "--verbose", 

223 dest="verbose", 

224 action="count", 

225 default=0, 

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

227) 

228 

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

230 Option, 

231 "--no-color", 

232 dest="no_color", 

233 action="store_true", 

234 default=False, 

235 help="Suppress colored output.", 

236) 

237 

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

239 Option, 

240 "-V", 

241 "--version", 

242 dest="version", 

243 action="store_true", 

244 help="Show version and exit.", 

245) 

246 

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

248 Option, 

249 "-q", 

250 "--quiet", 

251 dest="quiet", 

252 action="count", 

253 default=0, 

254 help=( 

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

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

257 " levels)." 

258 ), 

259) 

260 

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

262 Option, 

263 "--progress-bar", 

264 dest="progress_bar", 

265 type="choice", 

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

267 default="auto", 

268 help=( 

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

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

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

272 ), 

273) 

274 

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

276 PipOption, 

277 "--log", 

278 "--log-file", 

279 "--local-log", 

280 dest="log", 

281 metavar="path", 

282 type="path", 

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

284) 

285 

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

287 Option, 

288 # Don't ask for input 

289 "--no-input", 

290 dest="no_input", 

291 action="store_true", 

292 default=False, 

293 help="Disable prompting for input.", 

294) 

295 

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

297 Option, 

298 "--keyring-provider", 

299 dest="keyring_provider", 

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

301 default="auto", 

302 help=( 

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

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

305 " (default: %default)" 

306 ), 

307) 

308 

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

310 Option, 

311 "--proxy", 

312 dest="proxy", 

313 type="str", 

314 default="", 

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

316) 

317 

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

319 Option, 

320 "--retries", 

321 dest="retries", 

322 type="int", 

323 default=5, 

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

325) 

326 

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

328 Option, 

329 "--resume-retries", 

330 dest="resume_retries", 

331 type="int", 

332 default=5, 

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

334 "(default: %default)", 

335) 

336 

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

338 Option, 

339 "--timeout", 

340 "--default-timeout", 

341 metavar="sec", 

342 dest="timeout", 

343 type="float", 

344 default=15, 

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

346) 

347 

348 

349def exists_action() -> Option: 

350 return Option( 

351 # Option when path already exist 

352 "--exists-action", 

353 dest="exists_action", 

354 type="choice", 

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

356 default=[], 

357 action="append", 

358 metavar="action", 

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

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

361 ) 

362 

363 

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

365 PipOption, 

366 "--cert", 

367 dest="cert", 

368 type="path", 

369 metavar="path", 

370 help=( 

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

372 "If provided, overrides the default. " 

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

374 "for more information." 

375 ), 

376) 

377 

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

379 PipOption, 

380 "--client-cert", 

381 dest="client_cert", 

382 type="path", 

383 default=None, 

384 metavar="path", 

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

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

387) 

388 

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

390 Option, 

391 "-i", 

392 "--index-url", 

393 "--pypi-url", 

394 dest="index_url", 

395 metavar="URL", 

396 default=PyPI.simple_url, 

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

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

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

400 "in the same format.", 

401) 

402 

403 

404def extra_index_url() -> Option: 

405 return Option( 

406 "--extra-index-url", 

407 dest="extra_index_urls", 

408 metavar="URL", 

409 action="append", 

410 default=[], 

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

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

413 "--index-url.", 

414 ) 

415 

416 

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

418 Option, 

419 "--no-index", 

420 dest="no_index", 

421 action="store_true", 

422 default=False, 

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

424) 

425 

426 

427def find_links() -> Option: 

428 return Option( 

429 "-f", 

430 "--find-links", 

431 dest="find_links", 

432 action="append", 

433 default=[], 

434 metavar="url", 

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

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

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

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

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

440 ) 

441 

442 

443def _handle_uploaded_prior_to( 

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

445) -> None: 

446 """ 

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

448 

449 Accepts either an ISO 8601 datetime string (e.g., '2023-01-01T00:00:00Z') 

450 or a strict subset of ISO 8601 durations: PnD where n is a number of days 

451 (e.g., 'P7D' for 7 days ago). 

452 

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

454 as specified in the simple repository API: 

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

456 """ 

457 if value is None: 

458 return None 

459 

460 # Try ISO 8601 duration in PnD format. The leading 'P' disambiguates 

461 # from absolute datetimes. Only whole days are supported; the format may 

462 # be extended to more of the ISO 8601 duration syntax in the future if 

463 # a real need is presented. 

464 match = re.match(r"^P(\d+)D$", value, re.ASCII) 

465 if match: 

466 days = int(match.group(1)) 

467 parser.values.uploaded_prior_to = datetime.now(timezone.utc) - timedelta( 

468 days=days 

469 ) 

470 return 

471 

472 try: 

473 uploaded_prior_to = parse_iso_datetime(value) 

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

475 if uploaded_prior_to.tzinfo is None: 

476 uploaded_prior_to = uploaded_prior_to.astimezone() 

477 parser.values.uploaded_prior_to = uploaded_prior_to 

478 except ValueError as exc: 

479 msg = ( 

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

481 f"Expected an ISO 8601 datetime string " 

482 f"(e.g., '2023-01-01' or '2023-01-01T00:00:00Z') " 

483 f"or a duration in days (e.g., 'P3D')" 

484 ) 

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

486 

487 

488def uploaded_prior_to() -> Option: 

489 return Option( 

490 "--uploaded-prior-to", 

491 dest="uploaded_prior_to", 

492 metavar="datetime_or_duration", 

493 action="callback", 

494 callback=_handle_uploaded_prior_to, 

495 type="str", 

496 help=( 

497 "Only consider packages uploaded prior to the given value. " 

498 "Accepts an ISO 8601 datetime (e.g., '2023-01-01T00:00:00Z', " 

499 "uses local timezone if none specified) or a duration in days " 

500 "(e.g., 'P3D' for packages uploaded at least 3 days ago). " 

501 "Only effective when installing from indexes that provide " 

502 "upload-time metadata." 

503 ), 

504 ) 

505 

506 

507def trusted_host() -> Option: 

508 return Option( 

509 "--trusted-host", 

510 dest="trusted_hosts", 

511 action="append", 

512 metavar="HOSTNAME", 

513 default=[], 

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

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

516 ) 

517 

518 

519def constraints() -> Option: 

520 return Option( 

521 "-c", 

522 "--constraint", 

523 dest="constraints", 

524 action="append", 

525 default=[], 

526 metavar="file", 

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

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

529 ) 

530 

531 

532def build_constraints() -> Option: 

533 return Option( 

534 "--build-constraint", 

535 dest="build_constraints", 

536 action="append", 

537 type="str", 

538 default=[], 

539 metavar="file", 

540 help=( 

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

542 "This option can be used multiple times." 

543 ), 

544 ) 

545 

546 

547def requirements() -> Option: 

548 return Option( 

549 "-r", 

550 "--requirement", 

551 dest="requirements", 

552 action="append", 

553 default=[], 

554 metavar="file", 

555 help=( 

556 "Install from the given requirements file. " 

557 "The file or URL can be in pip's requirements.txt format, " 

558 "or pylock.toml format. pylock.toml support is experimental. " 

559 "This option can be used multiple times." 

560 ), 

561 ) 

562 

563 

564def requirements_from_scripts() -> Option: 

565 return Option( 

566 "--requirements-from-script", 

567 action="append", 

568 default=[], 

569 dest="requirements_from_scripts", 

570 metavar="file", 

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

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

573 ) 

574 

575 

576def editable() -> Option: 

577 return Option( 

578 "-e", 

579 "--editable", 

580 dest="editables", 

581 action="append", 

582 default=[], 

583 metavar="path/url", 

584 help=( 

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

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

587 ), 

588 ) 

589 

590 

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

592 value = os.path.abspath(value) 

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

594 

595 

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

597 PipOption, 

598 "--src", 

599 "--source", 

600 "--source-dir", 

601 "--source-directory", 

602 dest="src_dir", 

603 type="path", 

604 metavar="dir", 

605 default=get_src_prefix(), 

606 action="callback", 

607 callback=_handle_src, 

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

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

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

611) 

612 

613 

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

615 """Get a format_control object.""" 

616 return getattr(values, option.dest) 

617 

618 

619def _handle_no_binary( 

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

621) -> None: 

622 existing = _get_format_control(parser.values, option) 

623 FormatControl.handle_mutual_excludes( 

624 value, 

625 existing.no_binary, 

626 existing.only_binary, 

627 ) 

628 

629 

630def _handle_only_binary( 

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

632) -> None: 

633 existing = _get_format_control(parser.values, option) 

634 FormatControl.handle_mutual_excludes( 

635 value, 

636 existing.only_binary, 

637 existing.no_binary, 

638 ) 

639 

640 

641def no_binary() -> Option: 

642 format_control = FormatControl(set(), set()) 

643 return Option( 

644 "--no-binary", 

645 dest="format_control", 

646 action="callback", 

647 callback=_handle_no_binary, 

648 type="str", 

649 default=format_control, 

650 help="Do not download binary packages. Cached binary packages may still " 

651 "be used. Can be supplied multiple times, and each time adds to " 

652 "the existing value. Accepts either ':all:' to disable all binary " 

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

654 "or more package names with commas between them (no colons). " 

655 "Note that some packages are tricky to compile and may fail to " 

656 "install when this option is used on them.", 

657 ) 

658 

659 

660def only_binary() -> Option: 

661 format_control = FormatControl(set(), set()) 

662 return Option( 

663 "--only-binary", 

664 dest="format_control", 

665 action="callback", 

666 callback=_handle_only_binary, 

667 type="str", 

668 default=format_control, 

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

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

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

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

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

674 "option is used on them.", 

675 ) 

676 

677 

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

679 """Get a release_control object.""" 

680 return getattr(values, option.dest) 

681 

682 

683def _handle_all_releases( 

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

685) -> None: 

686 existing = _get_release_control(parser.values, option) 

687 existing.handle_mutual_excludes( 

688 value, 

689 existing.all_releases, 

690 existing.only_final, 

691 "all_releases", 

692 ) 

693 

694 

695def _handle_only_final( 

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

697) -> None: 

698 existing = _get_release_control(parser.values, option) 

699 existing.handle_mutual_excludes( 

700 value, 

701 existing.only_final, 

702 existing.all_releases, 

703 "only_final", 

704 ) 

705 

706 

707def all_releases() -> Option: 

708 release_control = ReleaseControl(set(), set()) 

709 return Option( 

710 "--all-releases", 

711 dest="release_control", 

712 action="callback", 

713 callback=_handle_all_releases, 

714 type="str", 

715 default=release_control, 

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

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

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

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

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

721 "used with --pre.", 

722 ) 

723 

724 

725def only_final() -> Option: 

726 release_control = ReleaseControl(set(), set()) 

727 return Option( 

728 "--only-final", 

729 dest="release_control", 

730 action="callback", 

731 callback=_handle_only_final, 

732 type="str", 

733 default=release_control, 

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

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

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

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

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

739 ) 

740 

741 

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

743 """ 

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

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

746 """ 

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

748 return 

749 

750 release_control = options.release_control 

751 if release_control.all_releases or release_control.only_final: 

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

753 

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

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

756 

757 

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

759 Option, 

760 "--platform", 

761 dest="platforms", 

762 metavar="platform", 

763 action="append", 

764 default=None, 

765 help=( 

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

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

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

769 ), 

770) 

771 

772 

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

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

775 """ 

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

777 

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

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

780 """ 

781 if not value: 

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

783 return (None, None) 

784 

785 parts = value.split(".") 

786 if len(parts) > 3: 

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

788 

789 if len(parts) == 1: 

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

791 value = parts[0] 

792 if len(value) > 1: 

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

794 

795 try: 

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

797 except ValueError: 

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

799 

800 return (version_info, None) 

801 

802 

803def _handle_python_version( 

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

805) -> None: 

806 """ 

807 Handle a provided --python-version value. 

808 """ 

809 version_info, error_msg = _convert_python_version(value) 

810 if error_msg is not None: 

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

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

813 

814 parser.values.python_version = version_info 

815 

816 

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

818 Option, 

819 "--python-version", 

820 dest="python_version", 

821 metavar="python_version", 

822 action="callback", 

823 callback=_handle_python_version, 

824 type="str", 

825 default=None, 

826 help=dedent("""\ 

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

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

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

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

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

832 """), 

833) 

834 

835 

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

837 Option, 

838 "--implementation", 

839 dest="implementation", 

840 metavar="implementation", 

841 default=None, 

842 help=( 

843 "Only use wheels compatible with Python " 

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

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

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

847 "implementation-agnostic wheels." 

848 ), 

849) 

850 

851 

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

853 Option, 

854 "--abi", 

855 dest="abis", 

856 metavar="abi", 

857 action="append", 

858 default=None, 

859 help=( 

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

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

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

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

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

865 "option." 

866 ), 

867) 

868 

869 

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

871 cmd_opts.add_option(platforms()) 

872 cmd_opts.add_option(python_version()) 

873 cmd_opts.add_option(implementation()) 

874 cmd_opts.add_option(abis()) 

875 

876 

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

878 target_python = TargetPython( 

879 platforms=options.platforms, 

880 py_version_info=options.python_version, 

881 abis=options.abis, 

882 implementation=options.implementation, 

883 ) 

884 

885 return target_python 

886 

887 

888def prefer_binary() -> Option: 

889 return Option( 

890 "--prefer-binary", 

891 dest="prefer_binary", 

892 action="store_true", 

893 default=False, 

894 help=( 

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

896 "source packages are newer." 

897 ), 

898 ) 

899 

900 

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

902 PipOption, 

903 "--cache-dir", 

904 dest="cache_dir", 

905 default=USER_CACHE_DIR, 

906 metavar="dir", 

907 type="path", 

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

909) 

910 

911 

912def _handle_no_cache_dir( 

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

914) -> None: 

915 """ 

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

917 

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

919 """ 

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

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

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

923 # environment variable, like PIP_NO_CACHE_DIR=true. 

924 if value is not None: 

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

926 try: 

927 strtobool(value) 

928 except ValueError as exc: 

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

930 

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

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

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

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

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

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

937 # some (valid) form. 

938 parser.values.cache_dir = False 

939 

940 

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

942 Option, 

943 "--no-cache-dir", 

944 dest="cache_dir", 

945 action="callback", 

946 callback=_handle_no_cache_dir, 

947 help="Disable the cache.", 

948) 

949 

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

951 Option, 

952 "--no-deps", 

953 "--no-dependencies", 

954 dest="ignore_dependencies", 

955 action="store_true", 

956 default=False, 

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

958) 

959 

960 

961def _handle_dependency_group( 

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

963) -> None: 

964 """ 

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

966 

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

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

969 

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

971 

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

973 """ 

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

975 if not sep: 

976 path = "pyproject.toml" 

977 else: 

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

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

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

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

982 

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

984 

985 

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

987 Option, 

988 "--group", 

989 dest="dependency_groups", 

990 default=[], 

991 type=str, 

992 action="callback", 

993 callback=_handle_dependency_group, 

994 metavar="[path:]group", 

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

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

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

998) 

999 

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

1001 Option, 

1002 "--ignore-requires-python", 

1003 dest="ignore_requires_python", 

1004 action="store_true", 

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

1006) 

1007 

1008 

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

1010 Option, 

1011 "--no-build-isolation", 

1012 dest="build_isolation", 

1013 action="store_false", 

1014 default=True, 

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

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

1017 "if this option is used.", 

1018) 

1019 

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

1021 Option, 

1022 "--check-build-dependencies", 

1023 dest="check_build_deps", 

1024 action="store_true", 

1025 default=False, 

1026 help="Check the build dependencies.", 

1027) 

1028 

1029 

1030use_pep517: Any = partial( 

1031 Option, 

1032 "--use-pep517", 

1033 dest="use_pep517", 

1034 action="store_true", 

1035 default=True, 

1036 help=SUPPRESS_HELP, 

1037) 

1038 

1039 

1040def _handle_config_settings( 

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

1042) -> None: 

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

1044 if sep != "=": 

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

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

1047 if dest is None: 

1048 dest = {} 

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

1050 if key in dest: 

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

1052 dest[key].append(val) 

1053 else: 

1054 dest[key] = [dest[key], val] 

1055 else: 

1056 dest[key] = val 

1057 

1058 

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

1060 Option, 

1061 "-C", 

1062 "--config-settings", 

1063 dest="config_settings", 

1064 type=str, 

1065 action="callback", 

1066 callback=_handle_config_settings, 

1067 metavar="settings", 

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

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

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

1071) 

1072 

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

1074 Option, 

1075 "--no-clean", 

1076 action="store_true", 

1077 default=False, 

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

1079) 

1080 

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

1082 Option, 

1083 "--pre", 

1084 action="store_true", 

1085 default=False, 

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

1087 "pip only finds stable versions.", 

1088) 

1089 

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

1091 Option, 

1092 "--json", 

1093 action="store_true", 

1094 default=False, 

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

1096) 

1097 

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

1099 Option, 

1100 "--disable-pip-version-check", 

1101 dest="disable_pip_version_check", 

1102 action="store_true", 

1103 default=False, 

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

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

1106) 

1107 

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

1109 Option, 

1110 "--root-user-action", 

1111 dest="root_user_action", 

1112 default="warn", 

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

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

1115) 

1116 

1117 

1118def _handle_merge_hash( 

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

1120) -> None: 

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

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

1123 if not parser.values.hashes: 

1124 parser.values.hashes = {} 

1125 try: 

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

1127 except ValueError: 

1128 parser.error( 

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

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

1131 "abcde..." 

1132 ) 

1133 if algo not in STRONG_HASHES: 

1134 parser.error( 

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

1136 opt_str, ", ".join(STRONG_HASHES) 

1137 ) 

1138 ) 

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

1140 

1141 

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

1143 Option, 

1144 "--hash", 

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

1146 # __dict__ copying in process_line(). 

1147 dest="hashes", 

1148 action="callback", 

1149 callback=_handle_merge_hash, 

1150 type="string", 

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

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

1153) 

1154 

1155 

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

1157 Option, 

1158 "--require-hashes", 

1159 dest="require_hashes", 

1160 action="store_true", 

1161 default=False, 

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

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

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

1165) 

1166 

1167 

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

1169 PipOption, 

1170 "--path", 

1171 dest="path", 

1172 type="path", 

1173 action="append", 

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

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

1176) 

1177 

1178 

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

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

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

1182 

1183 

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

1185 PipOption, 

1186 "--exclude", 

1187 dest="excludes", 

1188 action="append", 

1189 metavar="package", 

1190 type="package_name", 

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

1192) 

1193 

1194 

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

1196 Option, 

1197 "--no-python-version-warning", 

1198 dest="no_python_version_warning", 

1199 action="store_true", 

1200 default=False, 

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

1202) 

1203 

1204 

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

1206ALWAYS_ENABLED_FEATURES = [ 

1207 "truststore", # always on since 24.2 

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

1209] 

1210 

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

1212 Option, 

1213 "--use-feature", 

1214 dest="features_enabled", 

1215 metavar="feature", 

1216 action="append", 

1217 default=[], 

1218 choices=[ 

1219 "fast-deps", 

1220 "build-constraint", 

1221 "inprocess-build-deps", 

1222 ] 

1223 + ALWAYS_ENABLED_FEATURES, 

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

1225) 

1226 

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

1228 Option, 

1229 "--use-deprecated", 

1230 dest="deprecated_features_enabled", 

1231 metavar="feature", 

1232 action="append", 

1233 default=[], 

1234 choices=[ 

1235 "legacy-resolver", 

1236 "legacy-certs", 

1237 ], 

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

1239) 

1240 

1241########## 

1242# groups # 

1243########## 

1244 

1245general_group: dict[str, Any] = { 

1246 "name": "General Options", 

1247 "options": [ 

1248 help_, 

1249 debug_mode, 

1250 isolated_mode, 

1251 require_virtualenv, 

1252 python, 

1253 verbose, 

1254 version, 

1255 quiet, 

1256 log, 

1257 no_input, 

1258 keyring_provider, 

1259 proxy, 

1260 retries, 

1261 timeout, 

1262 exists_action, 

1263 trusted_host, 

1264 cert, 

1265 client_cert, 

1266 cache_dir, 

1267 no_cache, 

1268 disable_pip_version_check, 

1269 no_color, 

1270 no_python_version_warning, 

1271 use_new_feature, 

1272 use_deprecated_feature, 

1273 resume_retries, 

1274 ], 

1275} 

1276 

1277index_group: dict[str, Any] = { 

1278 "name": "Package Index Options", 

1279 "options": [ 

1280 index_url, 

1281 extra_index_url, 

1282 no_index, 

1283 find_links, 

1284 uploaded_prior_to, 

1285 ], 

1286} 

1287 

1288package_selection_group: dict[str, Any] = { 

1289 "name": "Package Selection Options", 

1290 "options": [ 

1291 pre, 

1292 all_releases, 

1293 only_final, 

1294 no_binary, 

1295 only_binary, 

1296 prefer_binary, 

1297 ], 

1298}