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.
- pip/__init__.py +1 -1
- pip/_internal/build_env.py +194 -5
- pip/_internal/cli/base_command.py +11 -0
- pip/_internal/cli/cmdoptions.py +157 -0
- pip/_internal/cli/index_command.py +20 -0
- pip/_internal/cli/main.py +11 -6
- pip/_internal/cli/main_parser.py +3 -1
- pip/_internal/cli/parser.py +93 -33
- pip/_internal/cli/progress_bars.py +4 -2
- pip/_internal/cli/req_command.py +99 -23
- pip/_internal/commands/cache.py +24 -0
- pip/_internal/commands/completion.py +2 -1
- pip/_internal/commands/download.py +8 -4
- pip/_internal/commands/index.py +13 -6
- pip/_internal/commands/install.py +36 -29
- pip/_internal/commands/list.py +14 -16
- pip/_internal/commands/lock.py +16 -8
- pip/_internal/commands/wheel.py +8 -13
- pip/_internal/exceptions.py +76 -3
- pip/_internal/index/collector.py +2 -3
- pip/_internal/index/package_finder.py +84 -18
- pip/_internal/locations/__init__.py +1 -2
- pip/_internal/locations/_sysconfig.py +4 -1
- pip/_internal/models/link.py +18 -14
- pip/_internal/models/release_control.py +92 -0
- pip/_internal/models/selection_prefs.py +6 -3
- pip/_internal/network/auth.py +6 -2
- pip/_internal/network/download.py +4 -5
- pip/_internal/network/session.py +14 -10
- pip/_internal/operations/install/wheel.py +1 -2
- pip/_internal/operations/prepare.py +2 -3
- pip/_internal/req/constructors.py +3 -1
- pip/_internal/req/pep723.py +41 -0
- pip/_internal/req/req_file.py +10 -1
- pip/_internal/resolution/resolvelib/factory.py +12 -1
- pip/_internal/resolution/resolvelib/requirements.py +7 -3
- pip/_internal/self_outdated_check.py +6 -13
- pip/_internal/utils/datetime.py +18 -0
- pip/_internal/utils/filesystem.py +40 -1
- pip/_internal/utils/logging.py +34 -2
- pip/_internal/utils/misc.py +18 -12
- pip/_internal/utils/pylock.py +116 -0
- pip/_internal/utils/unpacking.py +1 -1
- pip/_internal/vcs/versioncontrol.py +3 -1
- pip/_vendor/cachecontrol/__init__.py +6 -3
- pip/_vendor/cachecontrol/adapter.py +0 -1
- pip/_vendor/cachecontrol/controller.py +1 -1
- pip/_vendor/cachecontrol/filewrapper.py +3 -1
- pip/_vendor/certifi/__init__.py +1 -1
- pip/_vendor/certifi/cacert.pem +0 -332
- pip/_vendor/idna/LICENSE.md +1 -1
- pip/_vendor/idna/codec.py +1 -1
- pip/_vendor/idna/core.py +1 -1
- pip/_vendor/idna/idnadata.py +72 -6
- pip/_vendor/idna/package_data.py +1 -1
- pip/_vendor/idna/uts46data.py +891 -731
- pip/_vendor/packaging/__init__.py +1 -1
- pip/_vendor/packaging/_elffile.py +0 -1
- pip/_vendor/packaging/_manylinux.py +36 -36
- pip/_vendor/packaging/_musllinux.py +1 -1
- pip/_vendor/packaging/_parser.py +22 -10
- pip/_vendor/packaging/_structures.py +8 -0
- pip/_vendor/packaging/_tokenizer.py +23 -25
- pip/_vendor/packaging/licenses/__init__.py +13 -11
- pip/_vendor/packaging/licenses/_spdx.py +41 -1
- pip/_vendor/packaging/markers.py +64 -38
- pip/_vendor/packaging/metadata.py +143 -27
- pip/_vendor/packaging/pylock.py +635 -0
- pip/_vendor/packaging/requirements.py +5 -10
- pip/_vendor/packaging/specifiers.py +219 -170
- pip/_vendor/packaging/tags.py +15 -20
- pip/_vendor/packaging/utils.py +19 -24
- pip/_vendor/packaging/version.py +315 -105
- pip/_vendor/platformdirs/version.py +2 -2
- pip/_vendor/platformdirs/windows.py +7 -1
- pip/_vendor/vendor.txt +5 -5
- {pip-25.3.dist-info → pip-26.0.dist-info}/METADATA +2 -2
- {pip-25.3.dist-info → pip-26.0.dist-info}/RECORD +103 -100
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/AUTHORS.txt +18 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/idna/LICENSE.md +1 -1
- pip/_internal/models/pylock.py +0 -188
- {pip-25.3.dist-info → pip-26.0.dist-info}/WHEEL +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/entry_points.txt +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/LICENSE.txt +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/certifi/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/distlib/LICENSE.txt +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/distro/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/msgpack/COPYING +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.BSD +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/pkg_resources/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/platformdirs/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/pygments/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/requests/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/resolvelib/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/rich/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/tomli/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/tomli_w/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/truststore/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/urllib3/LICENSE.txt +0 -0
pip/__init__.py
CHANGED
pip/_internal/build_env.py
CHANGED
|
@@ -9,25 +9,37 @@ import site
|
|
|
9
9
|
import sys
|
|
10
10
|
import textwrap
|
|
11
11
|
from collections import OrderedDict
|
|
12
|
-
from collections.abc import Iterable
|
|
12
|
+
from collections.abc import Iterable, Sequence
|
|
13
|
+
from contextlib import AbstractContextManager as ContextManager
|
|
14
|
+
from contextlib import nullcontext
|
|
15
|
+
from io import StringIO
|
|
13
16
|
from types import TracebackType
|
|
14
17
|
from typing import TYPE_CHECKING, Protocol, TypedDict
|
|
15
18
|
|
|
16
19
|
from pip._vendor.packaging.version import Version
|
|
17
20
|
|
|
18
21
|
from pip import __file__ as pip_location
|
|
19
|
-
from pip._internal.cli.spinners import open_spinner
|
|
22
|
+
from pip._internal.cli.spinners import open_rich_spinner, open_spinner
|
|
23
|
+
from pip._internal.exceptions import (
|
|
24
|
+
BuildDependencyInstallError,
|
|
25
|
+
DiagnosticPipError,
|
|
26
|
+
InstallWheelBuildError,
|
|
27
|
+
PipError,
|
|
28
|
+
)
|
|
20
29
|
from pip._internal.locations import get_platlib, get_purelib, get_scheme
|
|
21
30
|
from pip._internal.metadata import get_default_environment, get_environment
|
|
22
31
|
from pip._internal.utils.deprecation import deprecated
|
|
23
|
-
from pip._internal.utils.logging import VERBOSE
|
|
32
|
+
from pip._internal.utils.logging import VERBOSE, capture_logging
|
|
24
33
|
from pip._internal.utils.packaging import get_requirement
|
|
25
34
|
from pip._internal.utils.subprocess import call_subprocess
|
|
26
35
|
from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
|
|
27
36
|
|
|
28
37
|
if TYPE_CHECKING:
|
|
38
|
+
from pip._internal.cache import WheelCache
|
|
29
39
|
from pip._internal.index.package_finder import PackageFinder
|
|
40
|
+
from pip._internal.operations.build.build_tracker import BuildTracker
|
|
30
41
|
from pip._internal.req.req_install import InstallRequirement
|
|
42
|
+
from pip._internal.resolution.base import BaseResolver
|
|
31
43
|
|
|
32
44
|
class ExtraEnviron(TypedDict, total=False):
|
|
33
45
|
extra_environ: dict[str, str]
|
|
@@ -188,6 +200,12 @@ class SubprocessBuildEnvironmentInstaller:
|
|
|
188
200
|
)
|
|
189
201
|
)
|
|
190
202
|
|
|
203
|
+
if finder.release_control is not None:
|
|
204
|
+
# Use ordered args to preserve the user's original command-line order
|
|
205
|
+
# This is important because later flags can override earlier ones
|
|
206
|
+
for attr_name, value in finder.release_control.get_ordered_args():
|
|
207
|
+
args.extend(("--" + attr_name.replace("_", "-"), value))
|
|
208
|
+
|
|
191
209
|
index_urls = finder.index_urls
|
|
192
210
|
if index_urls:
|
|
193
211
|
args.extend(["-i", index_urls[0]])
|
|
@@ -206,8 +224,6 @@ class SubprocessBuildEnvironmentInstaller:
|
|
|
206
224
|
args.extend(["--cert", finder.custom_cert])
|
|
207
225
|
if finder.client_cert:
|
|
208
226
|
args.extend(["--client-cert", finder.client_cert])
|
|
209
|
-
if finder.allow_all_prereleases:
|
|
210
|
-
args.append("--pre")
|
|
211
227
|
if finder.prefer_binary:
|
|
212
228
|
args.append("--prefer-binary")
|
|
213
229
|
|
|
@@ -230,6 +246,8 @@ class SubprocessBuildEnvironmentInstaller:
|
|
|
230
246
|
# in the isolated build environment
|
|
231
247
|
extra_environ = {"extra_environ": {"_PIP_IN_BUILD_IGNORE_CONSTRAINTS": "1"}}
|
|
232
248
|
|
|
249
|
+
if finder.uploaded_prior_to:
|
|
250
|
+
args.extend(["--uploaded-prior-to", finder.uploaded_prior_to.isoformat()])
|
|
233
251
|
args.append("--")
|
|
234
252
|
args.extend(requirements)
|
|
235
253
|
|
|
@@ -245,6 +263,177 @@ class SubprocessBuildEnvironmentInstaller:
|
|
|
245
263
|
)
|
|
246
264
|
|
|
247
265
|
|
|
266
|
+
class InprocessBuildEnvironmentInstaller:
|
|
267
|
+
"""
|
|
268
|
+
Build dependency installer that runs in the same pip process.
|
|
269
|
+
|
|
270
|
+
This contains a stripped down version of the install command with
|
|
271
|
+
only the logic necessary for installing build dependencies. The
|
|
272
|
+
finder, session, build tracker, and wheel cache are reused, but new
|
|
273
|
+
instances of everything else are created as needed.
|
|
274
|
+
|
|
275
|
+
Options are inherited from the parent install command unless
|
|
276
|
+
they don't make sense for build dependencies (in which case, they
|
|
277
|
+
are hard-coded, see comments below).
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
def __init__(
|
|
281
|
+
self,
|
|
282
|
+
*,
|
|
283
|
+
finder: PackageFinder,
|
|
284
|
+
build_tracker: BuildTracker,
|
|
285
|
+
wheel_cache: WheelCache,
|
|
286
|
+
build_constraints: Sequence[InstallRequirement] = (),
|
|
287
|
+
verbosity: int = 0,
|
|
288
|
+
) -> None:
|
|
289
|
+
from pip._internal.operations.prepare import RequirementPreparer
|
|
290
|
+
|
|
291
|
+
self._finder = finder
|
|
292
|
+
self._build_constraints = build_constraints
|
|
293
|
+
self._wheel_cache = wheel_cache
|
|
294
|
+
self._level = 0
|
|
295
|
+
|
|
296
|
+
build_dir = TempDirectory(kind="build-env-install", globally_managed=True)
|
|
297
|
+
self._preparer = RequirementPreparer(
|
|
298
|
+
build_isolation_installer=self,
|
|
299
|
+
# Inherited options or state.
|
|
300
|
+
finder=finder,
|
|
301
|
+
session=finder._link_collector.session,
|
|
302
|
+
build_dir=build_dir.path,
|
|
303
|
+
build_tracker=build_tracker,
|
|
304
|
+
verbosity=verbosity,
|
|
305
|
+
# This is irrelevant as it only applies to editable requirements.
|
|
306
|
+
src_dir="",
|
|
307
|
+
# Hard-coded options (that should NOT be inherited).
|
|
308
|
+
download_dir=None,
|
|
309
|
+
build_isolation=True,
|
|
310
|
+
check_build_deps=False,
|
|
311
|
+
progress_bar="off",
|
|
312
|
+
# TODO: hash-checking should be extended to build deps, but that is
|
|
313
|
+
# deferred for later as it'd be a breaking change.
|
|
314
|
+
require_hashes=False,
|
|
315
|
+
use_user_site=False,
|
|
316
|
+
lazy_wheel=False,
|
|
317
|
+
legacy_resolver=False,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
def install(
|
|
321
|
+
self,
|
|
322
|
+
requirements: Iterable[str],
|
|
323
|
+
prefix: _Prefix,
|
|
324
|
+
*,
|
|
325
|
+
kind: str,
|
|
326
|
+
for_req: InstallRequirement | None,
|
|
327
|
+
) -> None:
|
|
328
|
+
"""Install entrypoint. Manages output capturing and error handling."""
|
|
329
|
+
capture_logs = not logger.isEnabledFor(VERBOSE) and self._level == 0
|
|
330
|
+
if capture_logs:
|
|
331
|
+
# Hide the logs from the installation of build dependencies.
|
|
332
|
+
# They will be shown only if an error occurs.
|
|
333
|
+
capture_ctx: ContextManager[StringIO] = capture_logging()
|
|
334
|
+
spinner: ContextManager[None] = open_rich_spinner(f"Installing {kind}")
|
|
335
|
+
else:
|
|
336
|
+
# Otherwise, pass-through all logs (with a header).
|
|
337
|
+
capture_ctx, spinner = nullcontext(StringIO()), nullcontext()
|
|
338
|
+
logger.info("Installing %s ...", kind)
|
|
339
|
+
|
|
340
|
+
try:
|
|
341
|
+
self._level += 1
|
|
342
|
+
with spinner, capture_ctx as stream:
|
|
343
|
+
self._install_impl(requirements, prefix)
|
|
344
|
+
|
|
345
|
+
except DiagnosticPipError as exc:
|
|
346
|
+
# Format similar to a nested subprocess error, where the
|
|
347
|
+
# causing error is shown first, followed by the build error.
|
|
348
|
+
logger.info(textwrap.dedent(stream.getvalue()))
|
|
349
|
+
logger.error("%s", exc, extra={"rich": True})
|
|
350
|
+
logger.info("")
|
|
351
|
+
raise BuildDependencyInstallError(
|
|
352
|
+
for_req, requirements, cause=exc, log_lines=None
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
except Exception as exc:
|
|
356
|
+
logs: list[str] | None = textwrap.dedent(stream.getvalue()).splitlines()
|
|
357
|
+
if not capture_logs:
|
|
358
|
+
# If logs aren't being captured, then display the error inline
|
|
359
|
+
# with the rest of the logs.
|
|
360
|
+
logs = None
|
|
361
|
+
if isinstance(exc, PipError):
|
|
362
|
+
logger.error("%s", exc)
|
|
363
|
+
else:
|
|
364
|
+
logger.exception("pip crashed unexpectedly")
|
|
365
|
+
raise BuildDependencyInstallError(
|
|
366
|
+
for_req, requirements, cause=exc, log_lines=logs
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
finally:
|
|
370
|
+
self._level -= 1
|
|
371
|
+
|
|
372
|
+
def _install_impl(self, requirements: Iterable[str], prefix: _Prefix) -> None:
|
|
373
|
+
"""Core build dependency install logic."""
|
|
374
|
+
from pip._internal.commands.install import installed_packages_summary
|
|
375
|
+
from pip._internal.req import install_given_reqs
|
|
376
|
+
from pip._internal.req.constructors import install_req_from_line
|
|
377
|
+
from pip._internal.wheel_builder import build
|
|
378
|
+
|
|
379
|
+
ireqs = [install_req_from_line(req, user_supplied=True) for req in requirements]
|
|
380
|
+
ireqs.extend(self._build_constraints)
|
|
381
|
+
|
|
382
|
+
resolver = self._make_resolver()
|
|
383
|
+
resolved_set = resolver.resolve(ireqs, check_supported_wheels=True)
|
|
384
|
+
self._preparer.prepare_linked_requirements_more(
|
|
385
|
+
resolved_set.requirements.values()
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
reqs_to_build = [
|
|
389
|
+
r for r in resolved_set.requirements_to_install if not r.is_wheel
|
|
390
|
+
]
|
|
391
|
+
_, build_failures = build(reqs_to_build, self._wheel_cache, verify=True)
|
|
392
|
+
if build_failures:
|
|
393
|
+
raise InstallWheelBuildError(build_failures)
|
|
394
|
+
|
|
395
|
+
installed = install_given_reqs(
|
|
396
|
+
resolver.get_installation_order(resolved_set),
|
|
397
|
+
prefix=prefix.path,
|
|
398
|
+
# Hard-coded options (that should NOT be inherited).
|
|
399
|
+
root=None,
|
|
400
|
+
home=None,
|
|
401
|
+
warn_script_location=False,
|
|
402
|
+
use_user_site=False,
|
|
403
|
+
# As the build environment is ephemeral, it's wasteful to
|
|
404
|
+
# pre-compile everything since not all modules will be used.
|
|
405
|
+
pycompile=False,
|
|
406
|
+
progress_bar="off",
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
env = get_environment(list(prefix.lib_dirs))
|
|
410
|
+
if summary := installed_packages_summary(installed, env):
|
|
411
|
+
logger.info(summary)
|
|
412
|
+
|
|
413
|
+
def _make_resolver(self) -> BaseResolver:
|
|
414
|
+
"""Create a new resolver for one time use."""
|
|
415
|
+
# Legacy installer never used the legacy resolver so create a
|
|
416
|
+
# resolvelib resolver directly. Yuck.
|
|
417
|
+
from pip._internal.req.constructors import install_req_from_req_string
|
|
418
|
+
from pip._internal.resolution.resolvelib.resolver import Resolver
|
|
419
|
+
|
|
420
|
+
return Resolver(
|
|
421
|
+
make_install_req=install_req_from_req_string,
|
|
422
|
+
# Inherited state.
|
|
423
|
+
preparer=self._preparer,
|
|
424
|
+
finder=self._finder,
|
|
425
|
+
wheel_cache=self._wheel_cache,
|
|
426
|
+
# Hard-coded options (that should NOT be inherited).
|
|
427
|
+
ignore_requires_python=False,
|
|
428
|
+
use_user_site=False,
|
|
429
|
+
ignore_dependencies=False,
|
|
430
|
+
ignore_installed=True,
|
|
431
|
+
force_reinstall=False,
|
|
432
|
+
upgrade_strategy="to-satisfy-only",
|
|
433
|
+
py_version_info=None,
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
|
|
248
437
|
class BuildEnvironment:
|
|
249
438
|
"""Creates and manages an isolated environment to install build deps"""
|
|
250
439
|
|
|
@@ -235,6 +235,17 @@ class Command(CommandContextMixIn):
|
|
|
235
235
|
)
|
|
236
236
|
options.cache_dir = None
|
|
237
237
|
|
|
238
|
+
if (
|
|
239
|
+
"inprocess-build-deps" in options.features_enabled
|
|
240
|
+
and os.environ.get("PIP_CONSTRAINT", "")
|
|
241
|
+
and "build-constraint" not in options.features_enabled
|
|
242
|
+
):
|
|
243
|
+
logger.warning(
|
|
244
|
+
"In-process build dependencies are enabled, "
|
|
245
|
+
"PIP_CONSTRAINT will have no effect for build dependencies"
|
|
246
|
+
)
|
|
247
|
+
options.features_enabled.append("build-constraint")
|
|
248
|
+
|
|
238
249
|
return self._run_wrapper(level_number, options, args)
|
|
239
250
|
|
|
240
251
|
def handler_map(self) -> dict[str, Callable[[Values, list[str]], None]]:
|
pip/_internal/cli/cmdoptions.py
CHANGED
|
@@ -27,7 +27,9 @@ from pip._internal.exceptions import CommandError
|
|
|
27
27
|
from pip._internal.locations import USER_CACHE_DIR, get_src_prefix
|
|
28
28
|
from pip._internal.models.format_control import FormatControl
|
|
29
29
|
from pip._internal.models.index import PyPI
|
|
30
|
+
from pip._internal.models.release_control import ReleaseControl
|
|
30
31
|
from pip._internal.models.target_python import TargetPython
|
|
32
|
+
from pip._internal.utils.datetime import parse_iso_datetime
|
|
31
33
|
from pip._internal.utils.hashes import STRONG_HASHES
|
|
32
34
|
from pip._internal.utils.misc import strtobool
|
|
33
35
|
|
|
@@ -426,6 +428,54 @@ def find_links() -> Option:
|
|
|
426
428
|
)
|
|
427
429
|
|
|
428
430
|
|
|
431
|
+
def _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
|
+
|
|
462
|
+
def 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
|
+
|
|
429
479
|
def trusted_host() -> Option:
|
|
430
480
|
return Option(
|
|
431
481
|
"--trusted-host",
|
|
@@ -479,6 +529,18 @@ def requirements() -> Option:
|
|
|
479
529
|
)
|
|
480
530
|
|
|
481
531
|
|
|
532
|
+
def 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
|
+
|
|
482
544
|
def editable() -> Option:
|
|
483
545
|
return Option(
|
|
484
546
|
"-e",
|
|
@@ -580,6 +642,86 @@ def only_binary() -> Option:
|
|
|
580
642
|
)
|
|
581
643
|
|
|
582
644
|
|
|
645
|
+
def _get_release_control(values: Values, option: Option) -> Any:
|
|
646
|
+
"""Get a release_control object."""
|
|
647
|
+
return getattr(values, option.dest)
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
def _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
|
+
|
|
662
|
+
def _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
|
+
|
|
674
|
+
def 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
|
+
|
|
692
|
+
def 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
|
+
|
|
709
|
+
def 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
|
+
|
|
583
725
|
platforms: Callable[..., Option] = partial(
|
|
584
726
|
Option,
|
|
585
727
|
"--platform",
|
|
@@ -832,6 +974,7 @@ ignore_requires_python: Callable[..., Option] = partial(
|
|
|
832
974
|
help="Ignore the Requires-Python information.",
|
|
833
975
|
)
|
|
834
976
|
|
|
977
|
+
|
|
835
978
|
no_build_isolation: Callable[..., Option] = partial(
|
|
836
979
|
Option,
|
|
837
980
|
"--no-build-isolation",
|
|
@@ -1044,6 +1187,7 @@ use_new_feature: Callable[..., Option] = partial(
|
|
|
1044
1187
|
choices=[
|
|
1045
1188
|
"fast-deps",
|
|
1046
1189
|
"build-constraint",
|
|
1190
|
+
"inprocess-build-deps",
|
|
1047
1191
|
]
|
|
1048
1192
|
+ ALWAYS_ENABLED_FEATURES,
|
|
1049
1193
|
help="Enable new functionality, that may be backward incompatible.",
|
|
@@ -1106,5 +1250,18 @@ index_group: dict[str, Any] = {
|
|
|
1106
1250
|
extra_index_url,
|
|
1107
1251
|
no_index,
|
|
1108
1252
|
find_links,
|
|
1253
|
+
uploaded_prior_to,
|
|
1254
|
+
],
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
package_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,
|
|
1109
1266
|
],
|
|
1110
1267
|
}
|
|
@@ -23,6 +23,8 @@ from pip._internal.cli.command_context import CommandContextMixIn
|
|
|
23
23
|
if TYPE_CHECKING:
|
|
24
24
|
from ssl import SSLContext
|
|
25
25
|
|
|
26
|
+
from pip._vendor.packaging.utils import NormalizedName
|
|
27
|
+
|
|
26
28
|
from pip._internal.network.session import PipSession
|
|
27
29
|
|
|
28
30
|
logger = logging.getLogger(__name__)
|
|
@@ -103,6 +105,7 @@ class SessionCommandMixin(CommandContextMixIn):
|
|
|
103
105
|
session = PipSession(
|
|
104
106
|
cache=os.path.join(cache_dir, "http-v2") if cache_dir else None,
|
|
105
107
|
retries=retries if retries is not None else options.retries,
|
|
108
|
+
resume_retries=options.resume_retries,
|
|
106
109
|
trusted_hosts=options.trusted_hosts,
|
|
107
110
|
index_urls=self._get_index_urls(options),
|
|
108
111
|
ssl_context=ssl_context,
|
|
@@ -149,6 +152,23 @@ class IndexGroupCommand(Command, SessionCommandMixin):
|
|
|
149
152
|
This also corresponds to the commands that permit the pip version check.
|
|
150
153
|
"""
|
|
151
154
|
|
|
155
|
+
def should_exclude_prerelease(
|
|
156
|
+
self, options: Values, package_name: NormalizedName
|
|
157
|
+
) -> bool:
|
|
158
|
+
"""
|
|
159
|
+
Determine if pre-releases should be excluded for a package.
|
|
160
|
+
"""
|
|
161
|
+
# Check per-package release control settings
|
|
162
|
+
if options.release_control:
|
|
163
|
+
allow_prereleases = options.release_control.allows_prereleases(package_name)
|
|
164
|
+
if allow_prereleases is True:
|
|
165
|
+
return False # Include pre-releases
|
|
166
|
+
elif allow_prereleases is False:
|
|
167
|
+
return True # Exclude pre-releases
|
|
168
|
+
|
|
169
|
+
# No specific setting: exclude prereleases by default
|
|
170
|
+
return True
|
|
171
|
+
|
|
152
172
|
def handle_pip_version_check(self, options: Values) -> None:
|
|
153
173
|
"""
|
|
154
174
|
Do the pip version check if not disabled.
|
pip/_internal/cli/main.py
CHANGED
|
@@ -8,12 +8,6 @@ import os
|
|
|
8
8
|
import sys
|
|
9
9
|
import warnings
|
|
10
10
|
|
|
11
|
-
from pip._internal.cli.autocompletion import autocomplete
|
|
12
|
-
from pip._internal.cli.main_parser import parse_command
|
|
13
|
-
from pip._internal.commands import create_command
|
|
14
|
-
from pip._internal.exceptions import PipError
|
|
15
|
-
from pip._internal.utils import deprecation
|
|
16
|
-
|
|
17
11
|
logger = logging.getLogger(__name__)
|
|
18
12
|
|
|
19
13
|
|
|
@@ -45,6 +39,17 @@ logger = logging.getLogger(__name__)
|
|
|
45
39
|
|
|
46
40
|
|
|
47
41
|
def main(args: list[str] | None = None) -> int:
|
|
42
|
+
# NOTE: Lazy imports to speed up import of this module,
|
|
43
|
+
# which is imported from the pip console script. This doesn't
|
|
44
|
+
# speed up normal pip execution, but might be important in the future
|
|
45
|
+
# if we use ``multiprocessing`` module,
|
|
46
|
+
# which imports __main__ for each spawned subprocess.
|
|
47
|
+
from pip._internal.cli.autocompletion import autocomplete
|
|
48
|
+
from pip._internal.cli.main_parser import parse_command
|
|
49
|
+
from pip._internal.commands import create_command
|
|
50
|
+
from pip._internal.exceptions import PipError
|
|
51
|
+
from pip._internal.utils import deprecation
|
|
52
|
+
|
|
48
53
|
if args is None:
|
|
49
54
|
args = sys.argv[1:]
|
|
50
55
|
|
pip/_internal/cli/main_parser.py
CHANGED
|
@@ -6,6 +6,8 @@ import os
|
|
|
6
6
|
import subprocess
|
|
7
7
|
import sys
|
|
8
8
|
|
|
9
|
+
from pip._vendor.rich.markup import escape
|
|
10
|
+
|
|
9
11
|
from pip._internal.build_env import get_runnable_pip
|
|
10
12
|
from pip._internal.cli import cmdoptions
|
|
11
13
|
from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
|
|
@@ -39,7 +41,7 @@ def create_main_parser() -> ConfigOptionParser:
|
|
|
39
41
|
|
|
40
42
|
# create command listing for description
|
|
41
43
|
description = [""] + [
|
|
42
|
-
f"{name:27} {command_info.summary}"
|
|
44
|
+
f"[optparse.longargs]{name:27}[/] {escape(command_info.summary)}"
|
|
43
45
|
for name, command_info in commands_dict.items()
|
|
44
46
|
]
|
|
45
47
|
parser.description = "\n".join(description)
|