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
|
@@ -13,22 +13,33 @@ from __future__ import annotations
|
|
|
13
13
|
import abc
|
|
14
14
|
import itertools
|
|
15
15
|
import re
|
|
16
|
-
from typing import Callable, Iterable, Iterator, TypeVar, Union
|
|
16
|
+
from typing import Callable, Final, Iterable, Iterator, TypeVar, Union
|
|
17
17
|
|
|
18
18
|
from .utils import canonicalize_version
|
|
19
|
-
from .version import Version
|
|
19
|
+
from .version import InvalidVersion, Version
|
|
20
20
|
|
|
21
21
|
UnparsedVersion = Union[Version, str]
|
|
22
22
|
UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion)
|
|
23
23
|
CallableOperator = Callable[[Version, str], bool]
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
def _coerce_version(version: UnparsedVersion) -> Version:
|
|
26
|
+
def _coerce_version(version: UnparsedVersion) -> Version | None:
|
|
27
27
|
if not isinstance(version, Version):
|
|
28
|
-
|
|
28
|
+
try:
|
|
29
|
+
version = Version(version)
|
|
30
|
+
except InvalidVersion:
|
|
31
|
+
return None
|
|
29
32
|
return version
|
|
30
33
|
|
|
31
34
|
|
|
35
|
+
def _public_version(version: Version) -> Version:
|
|
36
|
+
return version.__replace__(local=None)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _base_version(version: Version) -> Version:
|
|
40
|
+
return version.__replace__(pre=None, post=None, dev=None, local=None)
|
|
41
|
+
|
|
42
|
+
|
|
32
43
|
class InvalidSpecifier(ValueError):
|
|
33
44
|
"""
|
|
34
45
|
Raised when attempting to create a :class:`Specifier` with a specifier
|
|
@@ -42,6 +53,14 @@ class InvalidSpecifier(ValueError):
|
|
|
42
53
|
|
|
43
54
|
|
|
44
55
|
class BaseSpecifier(metaclass=abc.ABCMeta):
|
|
56
|
+
__slots__ = ()
|
|
57
|
+
__match_args__ = ("_str",)
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def _str(self) -> str:
|
|
61
|
+
"""Internal property for match_args"""
|
|
62
|
+
return str(self)
|
|
63
|
+
|
|
45
64
|
@abc.abstractmethod
|
|
46
65
|
def __str__(self) -> str:
|
|
47
66
|
"""
|
|
@@ -73,7 +92,7 @@ class BaseSpecifier(metaclass=abc.ABCMeta):
|
|
|
73
92
|
prereleases or it can be set to ``None`` (the default) to use default semantics.
|
|
74
93
|
"""
|
|
75
94
|
|
|
76
|
-
@prereleases.setter
|
|
95
|
+
@prereleases.setter # noqa: B027
|
|
77
96
|
def prereleases(self, value: bool) -> None:
|
|
78
97
|
"""Setter for :attr:`prereleases`.
|
|
79
98
|
|
|
@@ -106,6 +125,8 @@ class Specifier(BaseSpecifier):
|
|
|
106
125
|
comma-separated version specifiers (which is what package metadata contains).
|
|
107
126
|
"""
|
|
108
127
|
|
|
128
|
+
__slots__ = ("_prereleases", "_spec", "_spec_version")
|
|
129
|
+
|
|
109
130
|
_operator_regex_str = r"""
|
|
110
131
|
(?P<operator>(~=|==|!=|<=|>=|<|>|===))
|
|
111
132
|
"""
|
|
@@ -204,11 +225,11 @@ class Specifier(BaseSpecifier):
|
|
|
204
225
|
"""
|
|
205
226
|
|
|
206
227
|
_regex = re.compile(
|
|
207
|
-
r"
|
|
228
|
+
r"\s*" + _operator_regex_str + _version_regex_str + r"\s*",
|
|
208
229
|
re.VERBOSE | re.IGNORECASE,
|
|
209
230
|
)
|
|
210
231
|
|
|
211
|
-
_operators = {
|
|
232
|
+
_operators: Final = {
|
|
212
233
|
"~=": "compatible",
|
|
213
234
|
"==": "equal",
|
|
214
235
|
"!=": "not_equal",
|
|
@@ -232,7 +253,7 @@ class Specifier(BaseSpecifier):
|
|
|
232
253
|
:raises InvalidSpecifier:
|
|
233
254
|
If the given specifier is invalid (i.e. bad syntax).
|
|
234
255
|
"""
|
|
235
|
-
match = self._regex.
|
|
256
|
+
match = self._regex.fullmatch(spec)
|
|
236
257
|
if not match:
|
|
237
258
|
raise InvalidSpecifier(f"Invalid specifier: {spec!r}")
|
|
238
259
|
|
|
@@ -244,33 +265,62 @@ class Specifier(BaseSpecifier):
|
|
|
244
265
|
# Store whether or not this Specifier should accept prereleases
|
|
245
266
|
self._prereleases = prereleases
|
|
246
267
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
268
|
+
# Specifier version cache
|
|
269
|
+
self._spec_version: tuple[str, Version] | None = None
|
|
270
|
+
|
|
271
|
+
def _get_spec_version(self, version: str) -> Version | None:
|
|
272
|
+
"""One element cache, as only one spec Version is needed per Specifier."""
|
|
273
|
+
if self._spec_version is not None and self._spec_version[0] == version:
|
|
274
|
+
return self._spec_version[1]
|
|
275
|
+
|
|
276
|
+
version_specifier = _coerce_version(version)
|
|
277
|
+
if version_specifier is None:
|
|
278
|
+
return None
|
|
279
|
+
|
|
280
|
+
self._spec_version = (version, version_specifier)
|
|
281
|
+
return version_specifier
|
|
282
|
+
|
|
283
|
+
def _require_spec_version(self, version: str) -> Version:
|
|
284
|
+
"""Get spec version, asserting it's valid (not for === operator).
|
|
285
|
+
|
|
286
|
+
This method should only be called for operators where version
|
|
287
|
+
strings are guaranteed to be valid PEP 440 versions (not ===).
|
|
288
|
+
"""
|
|
289
|
+
spec_version = self._get_spec_version(version)
|
|
290
|
+
assert spec_version is not None
|
|
291
|
+
return spec_version
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def prereleases(self) -> bool | None:
|
|
250
295
|
# If there is an explicit prereleases set for this, then we'll just
|
|
251
296
|
# blindly use that.
|
|
252
297
|
if self._prereleases is not None:
|
|
253
298
|
return self._prereleases
|
|
254
299
|
|
|
255
|
-
#
|
|
256
|
-
#
|
|
257
|
-
|
|
258
|
-
operator
|
|
259
|
-
|
|
260
|
-
#
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
#
|
|
266
|
-
|
|
267
|
-
if
|
|
300
|
+
# Only the "!=" operator does not imply prereleases when
|
|
301
|
+
# the version in the specifier is a prerelease.
|
|
302
|
+
operator, version_str = self._spec
|
|
303
|
+
if operator != "!=":
|
|
304
|
+
# The == specifier with trailing .* cannot include prereleases
|
|
305
|
+
# e.g. "==1.0a1.*" is not valid.
|
|
306
|
+
if operator == "==" and version_str.endswith(".*"):
|
|
307
|
+
return False
|
|
308
|
+
|
|
309
|
+
# "===" can have arbitrary string versions, so we cannot parse
|
|
310
|
+
# those, we take prereleases as unknown (None) for those.
|
|
311
|
+
version = self._get_spec_version(version_str)
|
|
312
|
+
if version is None:
|
|
313
|
+
return None
|
|
314
|
+
|
|
315
|
+
# For all other operators, use the check if spec Version
|
|
316
|
+
# object implies pre-releases.
|
|
317
|
+
if version.is_prerelease:
|
|
268
318
|
return True
|
|
269
319
|
|
|
270
320
|
return False
|
|
271
321
|
|
|
272
322
|
@prereleases.setter
|
|
273
|
-
def prereleases(self, value: bool) -> None:
|
|
323
|
+
def prereleases(self, value: bool | None) -> None:
|
|
274
324
|
self._prereleases = value
|
|
275
325
|
|
|
276
326
|
@property
|
|
@@ -321,11 +371,17 @@ class Specifier(BaseSpecifier):
|
|
|
321
371
|
|
|
322
372
|
@property
|
|
323
373
|
def _canonical_spec(self) -> tuple[str, str]:
|
|
374
|
+
operator, version = self._spec
|
|
375
|
+
if operator == "===" or version.endswith(".*"):
|
|
376
|
+
return operator, version
|
|
377
|
+
|
|
378
|
+
spec_version = self._require_spec_version(version)
|
|
379
|
+
|
|
324
380
|
canonical_version = canonicalize_version(
|
|
325
|
-
|
|
326
|
-
strip_trailing_zero=(self._spec[0] != "~="),
|
|
381
|
+
spec_version, strip_trailing_zero=(operator != "~=")
|
|
327
382
|
)
|
|
328
|
-
|
|
383
|
+
|
|
384
|
+
return operator, canonical_version
|
|
329
385
|
|
|
330
386
|
def __hash__(self) -> int:
|
|
331
387
|
return hash(self._canonical_spec)
|
|
@@ -390,7 +446,7 @@ class Specifier(BaseSpecifier):
|
|
|
390
446
|
if spec.endswith(".*"):
|
|
391
447
|
# In the case of prefix matching we want to ignore local segment.
|
|
392
448
|
normalized_prospective = canonicalize_version(
|
|
393
|
-
prospective
|
|
449
|
+
_public_version(prospective), strip_trailing_zero=False
|
|
394
450
|
)
|
|
395
451
|
# Get the normalized version string ignoring the trailing .*
|
|
396
452
|
normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False)
|
|
@@ -415,13 +471,13 @@ class Specifier(BaseSpecifier):
|
|
|
415
471
|
return shortened_prospective == split_spec
|
|
416
472
|
else:
|
|
417
473
|
# Convert our spec string into a Version
|
|
418
|
-
spec_version =
|
|
474
|
+
spec_version = self._require_spec_version(spec)
|
|
419
475
|
|
|
420
476
|
# If the specifier does not have a local segment, then we want to
|
|
421
477
|
# act as if the prospective version also does not have a local
|
|
422
478
|
# segment.
|
|
423
479
|
if not spec_version.local:
|
|
424
|
-
prospective =
|
|
480
|
+
prospective = _public_version(prospective)
|
|
425
481
|
|
|
426
482
|
return prospective == spec_version
|
|
427
483
|
|
|
@@ -432,18 +488,18 @@ class Specifier(BaseSpecifier):
|
|
|
432
488
|
# NB: Local version identifiers are NOT permitted in the version
|
|
433
489
|
# specifier, so local version labels can be universally removed from
|
|
434
490
|
# the prospective version.
|
|
435
|
-
return
|
|
491
|
+
return _public_version(prospective) <= self._require_spec_version(spec)
|
|
436
492
|
|
|
437
493
|
def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool:
|
|
438
494
|
# NB: Local version identifiers are NOT permitted in the version
|
|
439
495
|
# specifier, so local version labels can be universally removed from
|
|
440
496
|
# the prospective version.
|
|
441
|
-
return
|
|
497
|
+
return _public_version(prospective) >= self._require_spec_version(spec)
|
|
442
498
|
|
|
443
499
|
def _compare_less_than(self, prospective: Version, spec_str: str) -> bool:
|
|
444
500
|
# Convert our spec to a Version instance, since we'll want to work with
|
|
445
501
|
# it as a version.
|
|
446
|
-
spec =
|
|
502
|
+
spec = self._require_spec_version(spec_str)
|
|
447
503
|
|
|
448
504
|
# Check to see if the prospective version is less than the spec
|
|
449
505
|
# version. If it's not we can short circuit and just return False now
|
|
@@ -455,9 +511,12 @@ class Specifier(BaseSpecifier):
|
|
|
455
511
|
# includes is a pre-release version, that we do not accept pre-release
|
|
456
512
|
# versions for the version mentioned in the specifier (e.g. <3.1 should
|
|
457
513
|
# not match 3.1.dev0, but should match 3.0.dev0).
|
|
458
|
-
if
|
|
459
|
-
|
|
460
|
-
|
|
514
|
+
if (
|
|
515
|
+
not spec.is_prerelease
|
|
516
|
+
and prospective.is_prerelease
|
|
517
|
+
and _base_version(prospective) == _base_version(spec)
|
|
518
|
+
):
|
|
519
|
+
return False
|
|
461
520
|
|
|
462
521
|
# If we've gotten to here, it means that prospective version is both
|
|
463
522
|
# less than the spec version *and* it's not a pre-release of the same
|
|
@@ -467,7 +526,7 @@ class Specifier(BaseSpecifier):
|
|
|
467
526
|
def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool:
|
|
468
527
|
# Convert our spec to a Version instance, since we'll want to work with
|
|
469
528
|
# it as a version.
|
|
470
|
-
spec =
|
|
529
|
+
spec = self._require_spec_version(spec_str)
|
|
471
530
|
|
|
472
531
|
# Check to see if the prospective version is greater than the spec
|
|
473
532
|
# version. If it's not we can short circuit and just return False now
|
|
@@ -479,22 +538,26 @@ class Specifier(BaseSpecifier):
|
|
|
479
538
|
# includes is a post-release version, that we do not accept
|
|
480
539
|
# post-release versions for the version mentioned in the specifier
|
|
481
540
|
# (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0).
|
|
482
|
-
if
|
|
483
|
-
|
|
484
|
-
|
|
541
|
+
if (
|
|
542
|
+
not spec.is_postrelease
|
|
543
|
+
and prospective.is_postrelease
|
|
544
|
+
and _base_version(prospective) == _base_version(spec)
|
|
545
|
+
):
|
|
546
|
+
return False
|
|
485
547
|
|
|
486
548
|
# Ensure that we do not allow a local version of the version mentioned
|
|
487
549
|
# in the specifier, which is technically greater than, to match.
|
|
488
|
-
if prospective.local is not None
|
|
489
|
-
|
|
490
|
-
|
|
550
|
+
if prospective.local is not None and _base_version(
|
|
551
|
+
prospective
|
|
552
|
+
) == _base_version(spec):
|
|
553
|
+
return False
|
|
491
554
|
|
|
492
555
|
# If we've gotten to here, it means that prospective version is both
|
|
493
556
|
# greater than the spec version *and* it's not a pre-release of the
|
|
494
557
|
# same version in the spec.
|
|
495
558
|
return True
|
|
496
559
|
|
|
497
|
-
def _compare_arbitrary(self, prospective: Version, spec: str) -> bool:
|
|
560
|
+
def _compare_arbitrary(self, prospective: Version | str, spec: str) -> bool:
|
|
498
561
|
return str(prospective).lower() == str(spec).lower()
|
|
499
562
|
|
|
500
563
|
def __contains__(self, item: str | Version) -> bool:
|
|
@@ -512,7 +575,7 @@ class Specifier(BaseSpecifier):
|
|
|
512
575
|
>>> "1.0.0" in Specifier(">=1.2.3")
|
|
513
576
|
False
|
|
514
577
|
>>> "1.3.0a1" in Specifier(">=1.2.3")
|
|
515
|
-
|
|
578
|
+
True
|
|
516
579
|
>>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True)
|
|
517
580
|
True
|
|
518
581
|
"""
|
|
@@ -526,8 +589,8 @@ class Specifier(BaseSpecifier):
|
|
|
526
589
|
:class:`Version` instance.
|
|
527
590
|
:param prereleases:
|
|
528
591
|
Whether or not to match prereleases with this Specifier. If set to
|
|
529
|
-
``None`` (the default), it
|
|
530
|
-
|
|
592
|
+
``None`` (the default), it will follow the recommendation from
|
|
593
|
+
:pep:`440` and match prereleases, as there are no other versions.
|
|
531
594
|
|
|
532
595
|
>>> Specifier(">=1.2.3").contains("1.2.3")
|
|
533
596
|
True
|
|
@@ -536,31 +599,14 @@ class Specifier(BaseSpecifier):
|
|
|
536
599
|
>>> Specifier(">=1.2.3").contains("1.0.0")
|
|
537
600
|
False
|
|
538
601
|
>>> Specifier(">=1.2.3").contains("1.3.0a1")
|
|
539
|
-
False
|
|
540
|
-
>>> Specifier(">=1.2.3", prereleases=True).contains("1.3.0a1")
|
|
541
602
|
True
|
|
542
|
-
>>> Specifier(">=1.2.3").contains("1.3.0a1"
|
|
603
|
+
>>> Specifier(">=1.2.3", prereleases=False).contains("1.3.0a1")
|
|
604
|
+
False
|
|
605
|
+
>>> Specifier(">=1.2.3").contains("1.3.0a1")
|
|
543
606
|
True
|
|
544
607
|
"""
|
|
545
608
|
|
|
546
|
-
|
|
547
|
-
if prereleases is None:
|
|
548
|
-
prereleases = self.prereleases
|
|
549
|
-
|
|
550
|
-
# Normalize item to a Version, this allows us to have a shortcut for
|
|
551
|
-
# "2.0" in Specifier(">=2")
|
|
552
|
-
normalized_item = _coerce_version(item)
|
|
553
|
-
|
|
554
|
-
# Determine if we should be supporting prereleases in this specifier
|
|
555
|
-
# or not, if we do not support prereleases than we can short circuit
|
|
556
|
-
# logic if this version is a prereleases.
|
|
557
|
-
if normalized_item.is_prerelease and not prereleases:
|
|
558
|
-
return False
|
|
559
|
-
|
|
560
|
-
# Actually do the comparison to determine if this item is contained
|
|
561
|
-
# within this Specifier or not.
|
|
562
|
-
operator_callable: CallableOperator = self._get_operator(self.operator)
|
|
563
|
-
return operator_callable(normalized_item, self.version)
|
|
609
|
+
return bool(list(self.filter([item], prereleases=prereleases)))
|
|
564
610
|
|
|
565
611
|
def filter(
|
|
566
612
|
self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None
|
|
@@ -572,13 +618,8 @@ class Specifier(BaseSpecifier):
|
|
|
572
618
|
The items in the iterable will be filtered according to the specifier.
|
|
573
619
|
:param prereleases:
|
|
574
620
|
Whether or not to allow prereleases in the returned iterator. If set to
|
|
575
|
-
``None`` (the default), it will
|
|
576
|
-
|
|
577
|
-
whether the only versions matching are prereleases).
|
|
578
|
-
|
|
579
|
-
This method is smarter than just ``filter(Specifier().contains, [...])``
|
|
580
|
-
because it implements the rule from :pep:`440` that a prerelease item
|
|
581
|
-
SHOULD be accepted if no other versions match the given specifier.
|
|
621
|
+
``None`` (the default), it will follow the recommendation from :pep:`440`
|
|
622
|
+
and match prereleases if there are no other versions.
|
|
582
623
|
|
|
583
624
|
>>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))
|
|
584
625
|
['1.3']
|
|
@@ -591,40 +632,46 @@ class Specifier(BaseSpecifier):
|
|
|
591
632
|
>>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))
|
|
592
633
|
['1.3', '1.5a1']
|
|
593
634
|
"""
|
|
635
|
+
prereleases_versions = []
|
|
636
|
+
found_non_prereleases = False
|
|
594
637
|
|
|
595
|
-
|
|
596
|
-
|
|
638
|
+
# Determine if to include prereleases by default
|
|
639
|
+
include_prereleases = (
|
|
640
|
+
prereleases if prereleases is not None else self.prereleases
|
|
641
|
+
)
|
|
597
642
|
|
|
598
|
-
|
|
643
|
+
# Get the matching operator
|
|
644
|
+
operator_callable = self._get_operator(self.operator)
|
|
599
645
|
|
|
600
|
-
#
|
|
601
|
-
# them match, yield them.
|
|
646
|
+
# Filter versions
|
|
602
647
|
for version in iterable:
|
|
603
648
|
parsed_version = _coerce_version(version)
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
# else matches this specifier.
|
|
609
|
-
if parsed_version.is_prerelease and not (
|
|
610
|
-
prereleases or self.prereleases
|
|
649
|
+
if parsed_version is None:
|
|
650
|
+
# === operator can match arbitrary (non-version) strings
|
|
651
|
+
if self.operator == "===" and self._compare_arbitrary(
|
|
652
|
+
version, self.version
|
|
611
653
|
):
|
|
612
|
-
found_prereleases.append(version)
|
|
613
|
-
# Either this is not a prerelease, or we should have been
|
|
614
|
-
# accepting prereleases from the beginning.
|
|
615
|
-
else:
|
|
616
|
-
yielded = True
|
|
617
654
|
yield version
|
|
655
|
+
elif operator_callable(parsed_version, self.version):
|
|
656
|
+
# If it's not a prerelease or prereleases are allowed, yield it directly
|
|
657
|
+
if not parsed_version.is_prerelease or include_prereleases:
|
|
658
|
+
found_non_prereleases = True
|
|
659
|
+
yield version
|
|
660
|
+
# Otherwise collect prereleases for potential later use
|
|
661
|
+
elif prereleases is None and self._prereleases is not False:
|
|
662
|
+
prereleases_versions.append(version)
|
|
618
663
|
|
|
619
|
-
#
|
|
620
|
-
#
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
664
|
+
# If no non-prereleases were found and prereleases weren't
|
|
665
|
+
# explicitly forbidden, yield the collected prereleases
|
|
666
|
+
if (
|
|
667
|
+
not found_non_prereleases
|
|
668
|
+
and prereleases is None
|
|
669
|
+
and self._prereleases is not False
|
|
670
|
+
):
|
|
671
|
+
yield from prereleases_versions
|
|
625
672
|
|
|
626
673
|
|
|
627
|
-
_prefix_regex = re.compile(r"
|
|
674
|
+
_prefix_regex = re.compile(r"([0-9]+)((?:a|b|c|rc)[0-9]+)")
|
|
628
675
|
|
|
629
676
|
|
|
630
677
|
def _version_split(version: str) -> list[str]:
|
|
@@ -641,7 +688,7 @@ def _version_split(version: str) -> list[str]:
|
|
|
641
688
|
result.append(epoch or "0")
|
|
642
689
|
|
|
643
690
|
for item in rest.split("."):
|
|
644
|
-
match = _prefix_regex.
|
|
691
|
+
match = _prefix_regex.fullmatch(item)
|
|
645
692
|
if match:
|
|
646
693
|
result.extend(match.groups())
|
|
647
694
|
else:
|
|
@@ -694,6 +741,8 @@ class SpecifierSet(BaseSpecifier):
|
|
|
694
741
|
specifiers (``>=3.0,!=3.1``), or no specifier at all.
|
|
695
742
|
"""
|
|
696
743
|
|
|
744
|
+
__slots__ = ("_prereleases", "_specs")
|
|
745
|
+
|
|
697
746
|
def __init__(
|
|
698
747
|
self,
|
|
699
748
|
specifiers: str | Iterable[Specifier] = "",
|
|
@@ -747,10 +796,13 @@ class SpecifierSet(BaseSpecifier):
|
|
|
747
796
|
|
|
748
797
|
# Otherwise we'll see if any of the given specifiers accept
|
|
749
798
|
# prereleases, if any of them do we'll return True, otherwise False.
|
|
750
|
-
|
|
799
|
+
if any(s.prereleases for s in self._specs):
|
|
800
|
+
return True
|
|
801
|
+
|
|
802
|
+
return None
|
|
751
803
|
|
|
752
804
|
@prereleases.setter
|
|
753
|
-
def prereleases(self, value: bool) -> None:
|
|
805
|
+
def prereleases(self, value: bool | None) -> None:
|
|
754
806
|
self._prereleases = value
|
|
755
807
|
|
|
756
808
|
def __repr__(self) -> str:
|
|
@@ -810,9 +862,9 @@ class SpecifierSet(BaseSpecifier):
|
|
|
810
862
|
|
|
811
863
|
if self._prereleases is None and other._prereleases is not None:
|
|
812
864
|
specifier._prereleases = other._prereleases
|
|
813
|
-
elif
|
|
814
|
-
|
|
815
|
-
|
|
865
|
+
elif (
|
|
866
|
+
self._prereleases is not None and other._prereleases is None
|
|
867
|
+
) or self._prereleases == other._prereleases:
|
|
816
868
|
specifier._prereleases = self._prereleases
|
|
817
869
|
else:
|
|
818
870
|
raise ValueError(
|
|
@@ -876,7 +928,7 @@ class SpecifierSet(BaseSpecifier):
|
|
|
876
928
|
>>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1")
|
|
877
929
|
False
|
|
878
930
|
>>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1")
|
|
879
|
-
|
|
931
|
+
True
|
|
880
932
|
>>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)
|
|
881
933
|
True
|
|
882
934
|
"""
|
|
@@ -895,8 +947,11 @@ class SpecifierSet(BaseSpecifier):
|
|
|
895
947
|
:class:`Version` instance.
|
|
896
948
|
:param prereleases:
|
|
897
949
|
Whether or not to match prereleases with this SpecifierSet. If set to
|
|
898
|
-
``None`` (the default), it
|
|
899
|
-
|
|
950
|
+
``None`` (the default), it will follow the recommendation from :pep:`440`
|
|
951
|
+
and match prereleases, as there are no other versions.
|
|
952
|
+
:param installed:
|
|
953
|
+
Whether or not the item is installed. If set to ``True``, it will
|
|
954
|
+
accept prerelease versions even if the specifier does not allow them.
|
|
900
955
|
|
|
901
956
|
>>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3")
|
|
902
957
|
True
|
|
@@ -905,39 +960,19 @@ class SpecifierSet(BaseSpecifier):
|
|
|
905
960
|
>>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1")
|
|
906
961
|
False
|
|
907
962
|
>>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1")
|
|
908
|
-
False
|
|
909
|
-
>>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True).contains("1.3.0a1")
|
|
910
963
|
True
|
|
964
|
+
>>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False).contains("1.3.0a1")
|
|
965
|
+
False
|
|
911
966
|
>>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True)
|
|
912
967
|
True
|
|
913
968
|
"""
|
|
914
|
-
|
|
915
|
-
if not isinstance(item, Version):
|
|
916
|
-
item = Version(item)
|
|
917
|
-
|
|
918
|
-
# Determine if we're forcing a prerelease or not, if we're not forcing
|
|
919
|
-
# one for this particular filter call, then we'll use whatever the
|
|
920
|
-
# SpecifierSet thinks for whether or not we should support prereleases.
|
|
921
|
-
if prereleases is None:
|
|
922
|
-
prereleases = self.prereleases
|
|
969
|
+
version = _coerce_version(item)
|
|
923
970
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
# and this item is a pre-release then we do not allow it and we can
|
|
927
|
-
# short circuit that here.
|
|
928
|
-
# Note: This means that 1.0.dev1 would not be contained in something
|
|
929
|
-
# like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0
|
|
930
|
-
if not prereleases and item.is_prerelease:
|
|
931
|
-
return False
|
|
932
|
-
|
|
933
|
-
if installed and item.is_prerelease:
|
|
934
|
-
item = Version(item.base_version)
|
|
971
|
+
if version is not None and installed and version.is_prerelease:
|
|
972
|
+
prereleases = True
|
|
935
973
|
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
# Note: This use of all() here means that an empty set of specifiers
|
|
939
|
-
# will always return True, this is an explicit design decision.
|
|
940
|
-
return all(s.contains(item, prereleases=prereleases) for s in self._specs)
|
|
974
|
+
check_item = item if version is None else version
|
|
975
|
+
return bool(list(self.filter([check_item], prereleases=prereleases)))
|
|
941
976
|
|
|
942
977
|
def filter(
|
|
943
978
|
self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None
|
|
@@ -949,20 +984,15 @@ class SpecifierSet(BaseSpecifier):
|
|
|
949
984
|
The items in the iterable will be filtered according to the specifier.
|
|
950
985
|
:param prereleases:
|
|
951
986
|
Whether or not to allow prereleases in the returned iterator. If set to
|
|
952
|
-
``None`` (the default), it will
|
|
953
|
-
|
|
954
|
-
whether the only versions matching are prereleases).
|
|
955
|
-
|
|
956
|
-
This method is smarter than just ``filter(SpecifierSet(...).contains, [...])``
|
|
957
|
-
because it implements the rule from :pep:`440` that a prerelease item
|
|
958
|
-
SHOULD be accepted if no other versions match the given specifier.
|
|
987
|
+
``None`` (the default), it will follow the recommendation from :pep:`440`
|
|
988
|
+
and match prereleases if there are no other versions.
|
|
959
989
|
|
|
960
990
|
>>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))
|
|
961
991
|
['1.3']
|
|
962
992
|
>>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")]))
|
|
963
993
|
['1.3', <Version('1.4')>]
|
|
964
994
|
>>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"]))
|
|
965
|
-
[]
|
|
995
|
+
['1.5a1']
|
|
966
996
|
>>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))
|
|
967
997
|
['1.3', '1.5a1']
|
|
968
998
|
>>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))
|
|
@@ -983,37 +1013,56 @@ class SpecifierSet(BaseSpecifier):
|
|
|
983
1013
|
# Determine if we're forcing a prerelease or not, if we're not forcing
|
|
984
1014
|
# one for this particular filter call, then we'll use whatever the
|
|
985
1015
|
# SpecifierSet thinks for whether or not we should support prereleases.
|
|
986
|
-
if prereleases is None:
|
|
1016
|
+
if prereleases is None and self.prereleases is not None:
|
|
987
1017
|
prereleases = self.prereleases
|
|
988
1018
|
|
|
989
1019
|
# If we have any specifiers, then we want to wrap our iterable in the
|
|
990
1020
|
# filter method for each one, this will act as a logical AND amongst
|
|
991
1021
|
# each specifier.
|
|
992
1022
|
if self._specs:
|
|
1023
|
+
# When prereleases is None, we need to let all versions through
|
|
1024
|
+
# the individual filters, then decide about prereleases at the end
|
|
1025
|
+
# based on whether any non-prereleases matched ALL specs.
|
|
993
1026
|
for spec in self._specs:
|
|
994
|
-
iterable = spec.filter(
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1027
|
+
iterable = spec.filter(
|
|
1028
|
+
iterable, prereleases=True if prereleases is None else prereleases
|
|
1029
|
+
)
|
|
1030
|
+
|
|
1031
|
+
if prereleases is not None:
|
|
1032
|
+
# If we have a forced prereleases value,
|
|
1033
|
+
# we can immediately return the iterator.
|
|
1034
|
+
return iter(iterable)
|
|
999
1035
|
else:
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1036
|
+
# Handle empty SpecifierSet cases where prereleases is not None.
|
|
1037
|
+
if prereleases is True:
|
|
1038
|
+
return iter(iterable)
|
|
1039
|
+
|
|
1040
|
+
if prereleases is False:
|
|
1041
|
+
return (
|
|
1042
|
+
item
|
|
1043
|
+
for item in iterable
|
|
1044
|
+
if (version := _coerce_version(item)) is None
|
|
1045
|
+
or not version.is_prerelease
|
|
1046
|
+
)
|
|
1047
|
+
|
|
1048
|
+
# Finally if prereleases is None, apply PEP 440 logic:
|
|
1049
|
+
# exclude prereleases unless there are no final releases that matched.
|
|
1050
|
+
filtered_items: list[UnparsedVersionVar] = []
|
|
1051
|
+
found_prereleases: list[UnparsedVersionVar] = []
|
|
1052
|
+
found_final_release = False
|
|
1053
|
+
|
|
1054
|
+
for item in iterable:
|
|
1055
|
+
parsed_version = _coerce_version(item)
|
|
1056
|
+
# Arbitrary strings are always included as it is not
|
|
1057
|
+
# possible to determine if they are prereleases,
|
|
1058
|
+
# and they have already passed all specifiers.
|
|
1059
|
+
if parsed_version is None:
|
|
1060
|
+
filtered_items.append(item)
|
|
1061
|
+
found_prereleases.append(item)
|
|
1062
|
+
elif parsed_version.is_prerelease:
|
|
1063
|
+
found_prereleases.append(item)
|
|
1064
|
+
else:
|
|
1065
|
+
filtered_items.append(item)
|
|
1066
|
+
found_final_release = True
|
|
1067
|
+
|
|
1068
|
+
return iter(filtered_items if found_final_release else found_prereleases)
|