pip 25.3__py3-none-any.whl → 26.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. pip/__init__.py +1 -1
  2. pip/_internal/build_env.py +194 -5
  3. pip/_internal/cli/base_command.py +11 -0
  4. pip/_internal/cli/cmdoptions.py +157 -0
  5. pip/_internal/cli/index_command.py +20 -0
  6. pip/_internal/cli/main.py +11 -6
  7. pip/_internal/cli/main_parser.py +3 -1
  8. pip/_internal/cli/parser.py +93 -33
  9. pip/_internal/cli/progress_bars.py +4 -2
  10. pip/_internal/cli/req_command.py +99 -23
  11. pip/_internal/commands/cache.py +24 -0
  12. pip/_internal/commands/completion.py +2 -1
  13. pip/_internal/commands/download.py +8 -4
  14. pip/_internal/commands/index.py +13 -6
  15. pip/_internal/commands/install.py +36 -29
  16. pip/_internal/commands/list.py +14 -16
  17. pip/_internal/commands/lock.py +16 -8
  18. pip/_internal/commands/wheel.py +8 -13
  19. pip/_internal/exceptions.py +76 -3
  20. pip/_internal/index/collector.py +2 -3
  21. pip/_internal/index/package_finder.py +84 -18
  22. pip/_internal/locations/__init__.py +1 -2
  23. pip/_internal/locations/_sysconfig.py +4 -1
  24. pip/_internal/models/link.py +18 -14
  25. pip/_internal/models/release_control.py +92 -0
  26. pip/_internal/models/selection_prefs.py +6 -3
  27. pip/_internal/network/auth.py +6 -2
  28. pip/_internal/network/download.py +4 -5
  29. pip/_internal/network/session.py +14 -10
  30. pip/_internal/operations/install/wheel.py +1 -2
  31. pip/_internal/operations/prepare.py +2 -3
  32. pip/_internal/req/constructors.py +3 -1
  33. pip/_internal/req/pep723.py +41 -0
  34. pip/_internal/req/req_file.py +10 -1
  35. pip/_internal/resolution/resolvelib/factory.py +12 -1
  36. pip/_internal/resolution/resolvelib/requirements.py +7 -3
  37. pip/_internal/self_outdated_check.py +6 -13
  38. pip/_internal/utils/datetime.py +18 -0
  39. pip/_internal/utils/filesystem.py +40 -1
  40. pip/_internal/utils/logging.py +34 -2
  41. pip/_internal/utils/misc.py +18 -12
  42. pip/_internal/utils/pylock.py +116 -0
  43. pip/_internal/utils/unpacking.py +1 -1
  44. pip/_internal/vcs/versioncontrol.py +3 -1
  45. pip/_vendor/cachecontrol/__init__.py +6 -3
  46. pip/_vendor/cachecontrol/adapter.py +0 -1
  47. pip/_vendor/cachecontrol/controller.py +1 -1
  48. pip/_vendor/cachecontrol/filewrapper.py +3 -1
  49. pip/_vendor/certifi/__init__.py +1 -1
  50. pip/_vendor/certifi/cacert.pem +0 -332
  51. pip/_vendor/idna/LICENSE.md +1 -1
  52. pip/_vendor/idna/codec.py +1 -1
  53. pip/_vendor/idna/core.py +1 -1
  54. pip/_vendor/idna/idnadata.py +72 -6
  55. pip/_vendor/idna/package_data.py +1 -1
  56. pip/_vendor/idna/uts46data.py +891 -731
  57. pip/_vendor/packaging/__init__.py +1 -1
  58. pip/_vendor/packaging/_elffile.py +0 -1
  59. pip/_vendor/packaging/_manylinux.py +36 -36
  60. pip/_vendor/packaging/_musllinux.py +1 -1
  61. pip/_vendor/packaging/_parser.py +22 -10
  62. pip/_vendor/packaging/_structures.py +8 -0
  63. pip/_vendor/packaging/_tokenizer.py +23 -25
  64. pip/_vendor/packaging/licenses/__init__.py +13 -11
  65. pip/_vendor/packaging/licenses/_spdx.py +41 -1
  66. pip/_vendor/packaging/markers.py +64 -38
  67. pip/_vendor/packaging/metadata.py +143 -27
  68. pip/_vendor/packaging/pylock.py +635 -0
  69. pip/_vendor/packaging/requirements.py +5 -10
  70. pip/_vendor/packaging/specifiers.py +219 -170
  71. pip/_vendor/packaging/tags.py +15 -20
  72. pip/_vendor/packaging/utils.py +19 -24
  73. pip/_vendor/packaging/version.py +315 -105
  74. pip/_vendor/platformdirs/version.py +2 -2
  75. pip/_vendor/platformdirs/windows.py +7 -1
  76. pip/_vendor/vendor.txt +5 -5
  77. {pip-25.3.dist-info → pip-26.0.dist-info}/METADATA +2 -2
  78. {pip-25.3.dist-info → pip-26.0.dist-info}/RECORD +103 -100
  79. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/AUTHORS.txt +18 -0
  80. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/idna/LICENSE.md +1 -1
  81. pip/_internal/models/pylock.py +0 -188
  82. {pip-25.3.dist-info → pip-26.0.dist-info}/WHEEL +0 -0
  83. {pip-25.3.dist-info → pip-26.0.dist-info}/entry_points.txt +0 -0
  84. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/LICENSE.txt +0 -0
  85. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +0 -0
  86. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/certifi/LICENSE +0 -0
  87. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +0 -0
  88. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/distlib/LICENSE.txt +0 -0
  89. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/distro/LICENSE +0 -0
  90. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/msgpack/COPYING +0 -0
  91. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE +0 -0
  92. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +0 -0
  93. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.BSD +0 -0
  94. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/pkg_resources/LICENSE +0 -0
  95. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/platformdirs/LICENSE +0 -0
  96. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/pygments/LICENSE +0 -0
  97. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +0 -0
  98. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/requests/LICENSE +0 -0
  99. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/resolvelib/LICENSE +0 -0
  100. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/rich/LICENSE +0 -0
  101. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/tomli/LICENSE +0 -0
  102. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/tomli_w/LICENSE +0 -0
  103. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/truststore/LICENSE +0 -0
  104. {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/urllib3/LICENSE.txt +0 -0
@@ -34,11 +34,11 @@ from pip._internal.exceptions import (
34
34
  InstallWheelBuildError,
35
35
  )
36
36
  from pip._internal.locations import get_scheme
37
- from pip._internal.metadata import get_environment
37
+ from pip._internal.metadata import BaseEnvironment, get_environment
38
38
  from pip._internal.models.installation_report import InstallationReport
39
39
  from pip._internal.operations.build.build_tracker import get_build_tracker
40
40
  from pip._internal.operations.check import ConflictDetails, check_install_conflicts
41
- from pip._internal.req import install_given_reqs
41
+ from pip._internal.req import InstallationResult, install_given_reqs
42
42
  from pip._internal.req.req_install import (
43
43
  InstallRequirement,
44
44
  )
@@ -87,8 +87,8 @@ class InstallCommand(RequirementCommand):
87
87
  self.cmd_opts.add_option(cmdoptions.requirements())
88
88
  self.cmd_opts.add_option(cmdoptions.constraints())
89
89
  self.cmd_opts.add_option(cmdoptions.build_constraints())
90
+ self.cmd_opts.add_option(cmdoptions.requirements_from_scripts())
90
91
  self.cmd_opts.add_option(cmdoptions.no_deps())
91
- self.cmd_opts.add_option(cmdoptions.pre())
92
92
 
93
93
  self.cmd_opts.add_option(cmdoptions.editable())
94
94
  self.cmd_opts.add_option(
@@ -244,9 +244,6 @@ class InstallCommand(RequirementCommand):
244
244
  default=True,
245
245
  help="Do not warn about broken dependencies",
246
246
  )
247
- self.cmd_opts.add_option(cmdoptions.no_binary())
248
- self.cmd_opts.add_option(cmdoptions.only_binary())
249
- self.cmd_opts.add_option(cmdoptions.prefer_binary())
250
247
  self.cmd_opts.add_option(cmdoptions.require_hashes())
251
248
  self.cmd_opts.add_option(cmdoptions.progress_bar())
252
249
  self.cmd_opts.add_option(cmdoptions.root_user_action())
@@ -256,7 +253,13 @@ class InstallCommand(RequirementCommand):
256
253
  self.parser,
257
254
  )
258
255
 
256
+ selection_opts = cmdoptions.make_option_group(
257
+ cmdoptions.package_selection_group,
258
+ self.parser,
259
+ )
260
+
259
261
  self.parser.insert_option_group(0, index_opts)
262
+ self.parser.insert_option_group(0, selection_opts)
260
263
  self.parser.insert_option_group(0, self.cmd_opts)
261
264
 
262
265
  self.cmd_opts.add_option(
@@ -303,6 +306,7 @@ class InstallCommand(RequirementCommand):
303
306
 
304
307
  cmdoptions.check_build_constraints(options)
305
308
  cmdoptions.check_dist_restriction(options, check_target=True)
309
+ cmdoptions.check_release_control_exclusive(options)
306
310
 
307
311
  logger.verbose("Using %s", get_pip_version())
308
312
  options.use_user_site = decide_user_install(
@@ -475,34 +479,13 @@ class InstallCommand(RequirementCommand):
475
479
  )
476
480
  env = get_environment(lib_locations)
477
481
 
478
- # Display a summary of installed packages, with extra care to
479
- # display a package name as it was requested by the user.
480
- installed.sort(key=operator.attrgetter("name"))
481
- summary = []
482
- installed_versions = {}
483
- for distribution in env.iter_all_distributions():
484
- installed_versions[distribution.canonical_name] = distribution.version
485
- for package in installed:
486
- display_name = package.name
487
- version = installed_versions.get(canonicalize_name(display_name), None)
488
- if version:
489
- text = f"{display_name}-{version}"
490
- else:
491
- text = display_name
492
- summary.append(text)
493
-
494
482
  if conflicts is not None:
495
483
  self._warn_about_conflicts(
496
484
  conflicts,
497
485
  resolver_variant=self.determine_resolver_variant(options),
498
486
  )
499
-
500
- installed_desc = " ".join(summary)
501
- if installed_desc:
502
- write_output(
503
- "Successfully installed %s",
504
- installed_desc,
505
- )
487
+ if summary := installed_packages_summary(installed, env):
488
+ write_output(summary)
506
489
  except OSError as error:
507
490
  show_traceback = self.verbosity >= 1
508
491
 
@@ -641,6 +624,30 @@ class InstallCommand(RequirementCommand):
641
624
  logger.critical("\n".join(parts))
642
625
 
643
626
 
627
+ def installed_packages_summary(
628
+ installed: list[InstallationResult], env: BaseEnvironment
629
+ ) -> str:
630
+ # Format a summary of installed packages, with extra care to
631
+ # display a package name as it was requested by the user.
632
+ installed.sort(key=operator.attrgetter("name"))
633
+ summary = []
634
+ installed_versions = {}
635
+ for distribution in env.iter_all_distributions():
636
+ installed_versions[distribution.canonical_name] = distribution.version
637
+ for package in installed:
638
+ display_name = package.name
639
+ version = installed_versions.get(canonicalize_name(display_name), None)
640
+ if version:
641
+ text = f"{display_name}-{version}"
642
+ else:
643
+ text = display_name
644
+ summary.append(text)
645
+
646
+ if not summary:
647
+ return ""
648
+ return f"Successfully installed {' '.join(summary)}"
649
+
650
+
644
651
  def get_lib_location_guesses(
645
652
  user: bool = False,
646
653
  home: str | None = None,
@@ -90,15 +90,6 @@ class ListCommand(IndexGroupCommand):
90
90
  help="Only output packages installed in user-site.",
91
91
  )
92
92
  self.cmd_opts.add_option(cmdoptions.list_path())
93
- self.cmd_opts.add_option(
94
- "--pre",
95
- action="store_true",
96
- default=False,
97
- help=(
98
- "Include pre-release and development versions. By default, "
99
- "pip only finds stable versions."
100
- ),
101
- )
102
93
 
103
94
  self.cmd_opts.add_option(
104
95
  "--format",
@@ -135,7 +126,13 @@ class ListCommand(IndexGroupCommand):
135
126
  self.cmd_opts.add_option(cmdoptions.list_exclude())
136
127
  index_opts = cmdoptions.make_option_group(cmdoptions.index_group, self.parser)
137
128
 
129
+ selection_opts = cmdoptions.make_option_group(
130
+ cmdoptions.package_selection_group,
131
+ self.parser,
132
+ )
133
+
138
134
  self.parser.insert_option_group(0, index_opts)
135
+ self.parser.insert_option_group(0, selection_opts)
139
136
  self.parser.insert_option_group(0, self.cmd_opts)
140
137
 
141
138
  def handle_pip_version_check(self, options: Values) -> None:
@@ -157,7 +154,7 @@ class ListCommand(IndexGroupCommand):
157
154
  # Pass allow_yanked=False to ignore yanked versions.
158
155
  selection_prefs = SelectionPreferences(
159
156
  allow_yanked=False,
160
- allow_all_prereleases=options.pre,
157
+ release_control=options.release_control,
161
158
  )
162
159
 
163
160
  return PackageFinder.create(
@@ -166,6 +163,8 @@ class ListCommand(IndexGroupCommand):
166
163
  )
167
164
 
168
165
  def run(self, options: Values, args: list[str]) -> int:
166
+ cmdoptions.check_release_control_exclusive(options)
167
+
169
168
  if options.outdated and options.uptodate:
170
169
  raise CommandError("Options --outdated and --uptodate cannot be combined.")
171
170
 
@@ -248,8 +247,7 @@ class ListCommand(IndexGroupCommand):
248
247
  dist: _DistWithLatestInfo,
249
248
  ) -> _DistWithLatestInfo | None:
250
249
  all_candidates = finder.find_all_candidates(dist.canonical_name)
251
- if not options.pre:
252
- # Remove prereleases
250
+ if self.should_exclude_prerelease(options, dist.canonical_name):
253
251
  all_candidates = [
254
252
  candidate
255
253
  for candidate in all_candidates
@@ -341,15 +339,15 @@ def format_for_columns(
341
339
  if has_build_tags:
342
340
  header.append("Build")
343
341
 
342
+ has_editables = any(x.editable for x in pkgs)
343
+ if has_editables:
344
+ header.append("Editable project location")
345
+
344
346
  if options.verbose >= 1:
345
347
  header.append("Location")
346
348
  if options.verbose >= 1:
347
349
  header.append("Installer")
348
350
 
349
- has_editables = any(x.editable for x in pkgs)
350
- if has_editables:
351
- header.append("Editable project location")
352
-
353
351
  data = []
354
352
  for i, proj in enumerate(pkgs):
355
353
  # if we're working on the 'outdated' list, separate out the
@@ -2,6 +2,9 @@ import sys
2
2
  from optparse import Values
3
3
  from pathlib import Path
4
4
 
5
+ from pip._vendor import tomli_w
6
+ from pip._vendor.packaging.pylock import is_valid_pylock_path
7
+
5
8
  from pip._internal.cache import WheelCache
6
9
  from pip._internal.cli import cmdoptions
7
10
  from pip._internal.cli.req_command import (
@@ -9,12 +12,12 @@ from pip._internal.cli.req_command import (
9
12
  with_cleanup,
10
13
  )
11
14
  from pip._internal.cli.status_codes import SUCCESS
12
- from pip._internal.models.pylock import Pylock, is_valid_pylock_file_name
13
15
  from pip._internal.operations.build.build_tracker import get_build_tracker
14
16
  from pip._internal.utils.logging import getLogger
15
17
  from pip._internal.utils.misc import (
16
18
  get_pip_version,
17
19
  )
20
+ from pip._internal.utils.pylock import pylock_from_install_requirements
18
21
  from pip._internal.utils.temp_dir import TempDirectory
19
22
 
20
23
  logger = getLogger(__name__)
@@ -55,10 +58,10 @@ class LockCommand(RequirementCommand):
55
58
  )
56
59
  )
57
60
  self.cmd_opts.add_option(cmdoptions.requirements())
61
+ self.cmd_opts.add_option(cmdoptions.requirements_from_scripts())
58
62
  self.cmd_opts.add_option(cmdoptions.constraints())
59
63
  self.cmd_opts.add_option(cmdoptions.build_constraints())
60
64
  self.cmd_opts.add_option(cmdoptions.no_deps())
61
- self.cmd_opts.add_option(cmdoptions.pre())
62
65
 
63
66
  self.cmd_opts.add_option(cmdoptions.editable())
64
67
 
@@ -71,9 +74,6 @@ class LockCommand(RequirementCommand):
71
74
 
72
75
  self.cmd_opts.add_option(cmdoptions.config_settings())
73
76
 
74
- self.cmd_opts.add_option(cmdoptions.no_binary())
75
- self.cmd_opts.add_option(cmdoptions.only_binary())
76
- self.cmd_opts.add_option(cmdoptions.prefer_binary())
77
77
  self.cmd_opts.add_option(cmdoptions.require_hashes())
78
78
  self.cmd_opts.add_option(cmdoptions.progress_bar())
79
79
 
@@ -82,7 +82,13 @@ class LockCommand(RequirementCommand):
82
82
  self.parser,
83
83
  )
84
84
 
85
+ selection_opts = cmdoptions.make_option_group(
86
+ cmdoptions.package_selection_group,
87
+ self.parser,
88
+ )
89
+
85
90
  self.parser.insert_option_group(0, index_opts)
91
+ self.parser.insert_option_group(0, selection_opts)
86
92
  self.parser.insert_option_group(0, self.cmd_opts)
87
93
 
88
94
  @with_cleanup
@@ -96,6 +102,7 @@ class LockCommand(RequirementCommand):
96
102
  )
97
103
 
98
104
  cmdoptions.check_build_constraints(options)
105
+ cmdoptions.check_release_control_exclusive(options)
99
106
 
100
107
  session = self.get_default_session(options)
101
108
 
@@ -150,15 +157,16 @@ class LockCommand(RequirementCommand):
150
157
  base_dir = Path.cwd()
151
158
  else:
152
159
  output_file_path = Path(options.output_file)
153
- if not is_valid_pylock_file_name(output_file_path):
160
+ if not is_valid_pylock_path(output_file_path):
154
161
  logger.warning(
155
162
  "%s is not a valid lock file name.",
156
163
  output_file_path,
157
164
  )
158
165
  base_dir = output_file_path.parent
159
- pylock_toml = Pylock.from_install_requirements(
166
+ pylock = pylock_from_install_requirements(
160
167
  requirement_set.requirements.values(), base_dir=base_dir
161
- ).as_toml()
168
+ )
169
+ pylock_toml = tomli_w.dumps(pylock.to_dict())
162
170
  if options.output_file == "-":
163
171
  sys.stdout.write(pylock_toml)
164
172
  else:
@@ -51,9 +51,6 @@ class WheelCommand(RequirementCommand):
51
51
  "current working directory."
52
52
  ),
53
53
  )
54
- self.cmd_opts.add_option(cmdoptions.no_binary())
55
- self.cmd_opts.add_option(cmdoptions.only_binary())
56
- self.cmd_opts.add_option(cmdoptions.prefer_binary())
57
54
  self.cmd_opts.add_option(cmdoptions.no_build_isolation())
58
55
  self.cmd_opts.add_option(cmdoptions.use_pep517())
59
56
  self.cmd_opts.add_option(cmdoptions.check_build_deps())
@@ -61,6 +58,7 @@ class WheelCommand(RequirementCommand):
61
58
  self.cmd_opts.add_option(cmdoptions.build_constraints())
62
59
  self.cmd_opts.add_option(cmdoptions.editable())
63
60
  self.cmd_opts.add_option(cmdoptions.requirements())
61
+ self.cmd_opts.add_option(cmdoptions.requirements_from_scripts())
64
62
  self.cmd_opts.add_option(cmdoptions.src())
65
63
  self.cmd_opts.add_option(cmdoptions.ignore_requires_python())
66
64
  self.cmd_opts.add_option(cmdoptions.no_deps())
@@ -76,16 +74,6 @@ class WheelCommand(RequirementCommand):
76
74
 
77
75
  self.cmd_opts.add_option(cmdoptions.config_settings())
78
76
 
79
- self.cmd_opts.add_option(
80
- "--pre",
81
- action="store_true",
82
- default=False,
83
- help=(
84
- "Include pre-release and development versions. By default, "
85
- "pip only finds stable versions."
86
- ),
87
- )
88
-
89
77
  self.cmd_opts.add_option(cmdoptions.require_hashes())
90
78
 
91
79
  index_opts = cmdoptions.make_option_group(
@@ -93,12 +81,19 @@ class WheelCommand(RequirementCommand):
93
81
  self.parser,
94
82
  )
95
83
 
84
+ selection_opts = cmdoptions.make_option_group(
85
+ cmdoptions.package_selection_group,
86
+ self.parser,
87
+ )
88
+
96
89
  self.parser.insert_option_group(0, index_opts)
90
+ self.parser.insert_option_group(0, selection_opts)
97
91
  self.parser.insert_option_group(0, self.cmd_opts)
98
92
 
99
93
  @with_cleanup
100
94
  def run(self, options: Values, args: list[str]) -> int:
101
95
  cmdoptions.check_build_constraints(options)
96
+ cmdoptions.check_release_control_exclusive(options)
102
97
 
103
98
  session = self.get_default_session(options)
104
99
 
@@ -14,7 +14,8 @@ import logging
14
14
  import pathlib
15
15
  import re
16
16
  import sys
17
- from collections.abc import Iterator
17
+ import traceback
18
+ from collections.abc import Iterable, Iterator
18
19
  from itertools import chain, groupby, repeat
19
20
  from typing import TYPE_CHECKING, Literal
20
21
 
@@ -27,9 +28,10 @@ from pip._vendor.rich.text import Text
27
28
  if TYPE_CHECKING:
28
29
  from hashlib import _Hash
29
30
 
30
- from pip._vendor.requests.models import Request, Response
31
+ from pip._vendor.requests.models import PreparedRequest, Request, Response
31
32
 
32
33
  from pip._internal.metadata import BaseDistribution
34
+ from pip._internal.models.link import Link
33
35
  from pip._internal.network.download import _FileDownload
34
36
  from pip._internal.req.req_install import InstallRequirement
35
37
 
@@ -314,7 +316,7 @@ class NetworkConnectionError(PipError):
314
316
  self,
315
317
  error_msg: str,
316
318
  response: Response | None = None,
317
- request: Request | None = None,
319
+ request: Request | PreparedRequest | None = None,
318
320
  ) -> None:
319
321
  """
320
322
  Initialize NetworkConnectionError with `request` and `response`
@@ -896,3 +898,74 @@ class InstallWheelBuildError(DiagnosticPipError):
896
898
  context=", ".join(r.name for r in failed), # type: ignore
897
899
  hint_stmt=None,
898
900
  )
901
+
902
+
903
+ class InvalidEggFragment(DiagnosticPipError):
904
+ reference = "invalid-egg-fragment"
905
+
906
+ def __init__(self, link: Link, fragment: str) -> None:
907
+ hint = ""
908
+ if ">" in fragment or "=" in fragment or "<" in fragment:
909
+ hint = (
910
+ "Version specifiers are silently ignored for URL references. "
911
+ "Remove them. "
912
+ )
913
+ if "[" in fragment and "]" in fragment:
914
+ hint += "Try using the Direct URL requirement syntax: 'name[extra] @ URL'"
915
+
916
+ if not hint:
917
+ hint = "Egg fragments can only be a valid project name."
918
+
919
+ super().__init__(
920
+ message=f"The '{escape(fragment)}' egg fragment is invalid",
921
+ context=f"from '{escape(str(link))}'",
922
+ hint_stmt=escape(hint),
923
+ )
924
+
925
+
926
+ class BuildDependencyInstallError(DiagnosticPipError):
927
+ """Raised when build dependencies cannot be installed."""
928
+
929
+ reference = "failed-build-dependency-install"
930
+
931
+ def __init__(
932
+ self,
933
+ req: InstallRequirement | None,
934
+ build_reqs: Iterable[str],
935
+ *,
936
+ cause: Exception,
937
+ log_lines: list[str] | None,
938
+ ) -> None:
939
+ if isinstance(cause, PipError):
940
+ note = "This is likely not a problem with pip."
941
+ else:
942
+ note = (
943
+ "pip crashed unexpectedly. Please file an issue on pip's issue "
944
+ "tracker: https://github.com/pypa/pip/issues/new"
945
+ )
946
+
947
+ if log_lines is None:
948
+ # No logs are available, they must have been printed earlier.
949
+ context = Text("See above for more details.")
950
+ else:
951
+ if isinstance(cause, PipError):
952
+ log_lines.append(f"ERROR: {cause}")
953
+ else:
954
+ # Split rendered error into real lines without trailing newlines.
955
+ log_lines.extend(
956
+ "".join(traceback.format_exception(cause)).splitlines()
957
+ )
958
+
959
+ context = Text.assemble(
960
+ f"Installing {' '.join(build_reqs)}\n",
961
+ (f"[{len(log_lines)} lines of output]\n", "red"),
962
+ "\n".join(log_lines),
963
+ ("\n[end of output]", "red"),
964
+ )
965
+
966
+ message = Text("Cannot install build dependencies", "green")
967
+ if req:
968
+ message += Text(f" for {req}")
969
+ super().__init__(
970
+ message=message, context=context, hint_stmt=None, note_stmt=note
971
+ )
@@ -12,7 +12,6 @@ import json
12
12
  import logging
13
13
  import os
14
14
  import urllib.parse
15
- import urllib.request
16
15
  from collections.abc import Iterable, MutableMapping, Sequence
17
16
  from dataclasses import dataclass
18
17
  from html.parser import HTMLParser
@@ -34,6 +33,7 @@ from pip._internal.network.session import PipSession
34
33
  from pip._internal.network.utils import raise_for_status
35
34
  from pip._internal.utils.filetypes import is_archive_file
36
35
  from pip._internal.utils.misc import redact_auth_from_url
36
+ from pip._internal.utils.urls import url_to_path
37
37
  from pip._internal.vcs import vcs
38
38
 
39
39
  from .sources import CandidatesFromPage, LinkSource, build_source
@@ -330,8 +330,7 @@ def _get_index_content(link: Link, *, session: PipSession) -> IndexContent | Non
330
330
  return None
331
331
 
332
332
  # Tack index.html onto file:// URLs that point to directories
333
- scheme, _, path, _, _, _ = urllib.parse.urlparse(url)
334
- if scheme == "file" and os.path.isdir(urllib.request.url2pathname(path)):
333
+ if url.startswith("file:") and os.path.isdir(url_to_path(url)):
335
334
  # add trailing slash if not present so urljoin doesn't trim
336
335
  # final segment
337
336
  if not url.endswith("/"):