Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit5c6963f

Browse files
authored
Fix prerelease versions being selected without --pre flag (#6484)
* Fix prerelease versions being selected without --pre flagWhen a transitive dependency specifies a prerelease version in itsrequirements (e.g., pottery requires redis>=4.2.0rc1), pip's SpecifierSetwould enable prereleases for all packages, causing prerelease versionsto be selected even when the user didn't request them with --pre.The fix changes the prerelease filtering logic in package_finder.py toexplicitly pass prereleases=False instead of None when --pre is notspecified. This prevents transitive dependencies with prereleasespecifiers from enabling prereleases for all packages.When prereleases=False, if no stable versions satisfy the constraints,the resolver will fail rather than silently selecting a prerelease,which is the expected behavior when the user hasn't opted into prereleases.Fixes#6395* Remove unused pytest import* Fix PLR2004 linting error: use constant for magic value
1 parent1093b07 commit5c6963f

File tree

4 files changed

+136
-4
lines changed

4 files changed

+136
-4
lines changed

‎pipenv/patched/pip/_internal/index/package_finder.py‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,8 +458,14 @@ def get_applicable_candidates(
458458
"""
459459
Return the applicable candidates from a list of candidates.
460460
"""
461-
# Using None infers from the specifier instead.
462-
allow_prereleases=self._allow_all_prereleasesorNone
461+
# Pipenv patch: Use explicit False instead of None when prereleases
462+
# are not requested. This prevents transitive dependencies with
463+
# prerelease specifiers (e.g., ">=4.2.0rc1") from enabling prereleases
464+
# for all packages. When prereleases=False, if no stable versions
465+
# satisfy the constraints, the resolver will fail rather than silently
466+
# selecting a prerelease.
467+
# See: https://github.com/pypa/pipenv/issues/6395
468+
allow_prereleases=Trueifself._allow_all_prereleaseselseFalse
463469
specifier=self._specifier
464470

465471
# We turn the version object into a str here because otherwise
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
diff --git a/pipenv/patched/pip/_internal/index/package_finder.py b/pipenv/patched/pip/_internal/index/package_finder.py
2+
index 1234567..abcdefg 100644
3+
--- a/pipenv/patched/pip/_internal/index/package_finder.py
4+
+++ b/pipenv/patched/pip/_internal/index/package_finder.py
5+
@@ -458,8 +458,14 @@ class CandidateEvaluator:
6+
) -> list[InstallationCandidate]:
7+
"""
8+
Return the applicable candidates from a list of candidates.
9+
"""
10+
- # Using None infers from the specifier instead.
11+
- allow_prereleases = self._allow_all_prereleases or None
12+
+ # Pipenv patch: Use explicit False instead of None when prereleases
13+
+ # are not requested. This prevents transitive dependencies with
14+
+ # prerelease specifiers (e.g., ">=4.2.0rc1") from enabling prereleases
15+
+ # for all packages. When prereleases=False, if no stable versions
16+
+ # satisfy the constraints, the resolver will fail rather than silently
17+
+ # selecting a prerelease.
18+
+ # See: https://github.com/pypa/pipenv/issues/6395
19+
+ allow_prereleases = True if self._allow_all_prereleases else False
20+
specifier = self._specifier
21+
22+
# We turn the version object into a str here because otherwise
23+

‎tests/unit/test_dependencies.py‎

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
frompipenv.utils.dependenciesimportclean_resolved_dep
2+
frompipenv.vendor.packaging.specifiersimportSpecifierSet
23

34

45
deftest_clean_resolved_dep_with_vcs_url():
@@ -29,3 +30,104 @@ def test_clean_resolved_dep_with_vcs_url_and_extras():
2930
assertresult["example-package"]["git"]=="git+https://${GIT_USERNAME}:${GIT_PASSWORD}@github.com/username/repo.git[extra1,extra2]"
3031
assertresult["example-package"]["ref"]=="main"
3132
assertresult["example-package"]["extras"]== ["extra1","extra2"]
33+
34+
35+
classTestPrereleaseFiltering:
36+
"""Tests for prerelease version filtering behavior.
37+
38+
These tests verify the fix for https://github.com/pypa/pipenv/issues/6395
39+
where transitive dependencies with prerelease specifiers (e.g., ">=4.2.0rc1")
40+
would cause prereleases to be selected even when the user didn't request them.
41+
"""
42+
43+
deftest_specifier_with_prerelease_version_enables_prereleases(self):
44+
"""Verify that a specifier containing a prerelease version has prereleases=True.
45+
46+
This is the root cause of issue #6395 - when a transitive dependency
47+
specifies a prerelease version (e.g., ">=4.2.0rc1"), the SpecifierSet's
48+
prereleases property returns True.
49+
"""
50+
# A specifier with a prerelease version has prereleases=True
51+
spec_with_prerelease=SpecifierSet(">=4.2.0rc1")
52+
assertspec_with_prerelease.prereleasesisTrue
53+
54+
# A specifier without a prerelease version has prereleases=False
55+
# (not None, because SpecifierSet.prereleases returns any(s.prereleases for s in self._specs))
56+
spec_without_prerelease=SpecifierSet(">=4.2.0")
57+
assertspec_without_prerelease.prereleasesisFalse
58+
59+
deftest_filter_with_explicit_false_excludes_prereleases(self):
60+
"""Verify that filter(prereleases=False) excludes prereleases when stable versions exist."""
61+
spec=SpecifierSet(">=4.2.0rc1")
62+
versions= ["4.2.0rc1","4.2.0","4.3.0","5.0.0b1","5.0.0"]
63+
64+
# With prereleases=False, only stable versions should be returned
65+
filtered=list(spec.filter(versions,prereleases=False))
66+
assert"4.2.0rc1"notinfiltered
67+
assert"5.0.0b1"notinfiltered
68+
assert"4.2.0"infiltered
69+
assert"4.3.0"infiltered
70+
assert"5.0.0"infiltered
71+
72+
deftest_filter_with_explicit_false_no_fallback(self):
73+
"""Verify that filter(prereleases=False) does NOT fall back to prereleases.
74+
75+
When prereleases=False is explicitly passed, prereleases are excluded
76+
even if no stable versions match. This is the expected behavior for
77+
pipenv when --pre is not specified - if no stable versions satisfy
78+
the constraints, the resolver should fail rather than silently
79+
selecting a prerelease.
80+
"""
81+
spec=SpecifierSet(">=6.0.0")
82+
versions= ["4.2.0","5.0.0","6.0.0b1","6.0.0rc1"]
83+
84+
# With prereleases=False, no versions should be returned (no stable versions match)
85+
filtered=list(spec.filter(versions,prereleases=False))
86+
assertfiltered== []
87+
88+
deftest_filter_with_none_uses_specifier_prereleases(self):
89+
"""Verify that filter(prereleases=None) defers to the specifier's prereleases property.
90+
91+
This is the problematic behavior that issue #6395 addresses - when
92+
prereleases=None is passed, the specifier's own prereleases property
93+
is used, which returns True if the specifier contains a prerelease version.
94+
"""
95+
spec=SpecifierSet(">=4.2.0rc1")
96+
versions= ["4.2.0rc1","4.2.0","4.3.0","5.0.0b1","5.0.0"]
97+
98+
# With prereleases=None, the specifier's prereleases property is used
99+
# Since spec.prereleases is True (due to "rc1" in the specifier),
100+
# prereleases will be included
101+
filtered=list(spec.filter(versions,prereleases=None))
102+
assert"4.2.0rc1"infiltered
103+
assert"5.0.0b1"infiltered
104+
105+
deftest_issue_6395_transitive_prerelease_specifier(self):
106+
"""Test the exact scenario from issue #6395.
107+
108+
When a transitive dependency specifies a prerelease version (e.g., pottery
109+
requires redis>=4.2.0rc1), the combined specifier's prereleases property
110+
returns True. If we pass prereleases=None to filter(), prereleases will
111+
be included. But if we pass prereleases=False, they will be excluded.
112+
"""
113+
# Simulate the combined specifier from issue #6395:
114+
# User wants redis>=4.5.0, transitive dep wants redis>=4.2.0rc1
115+
user_spec=SpecifierSet(">=4.5.0")
116+
transitive_spec=SpecifierSet(">=4.2.0rc1")
117+
combined=user_spec&transitive_spec
118+
119+
# The combined specifier has prereleases=True due to the transitive dep
120+
assertcombined.prereleasesisTrue
121+
122+
versions= ["4.5.0","5.0.0","5.3.0b5","5.2.0"]
123+
124+
# With prereleases=None (the old behavior), prereleases are included
125+
filtered_none=list(combined.filter(versions,prereleases=None))
126+
assert"5.3.0b5"infiltered_none
127+
128+
# With prereleases=False (the fix), prereleases are excluded
129+
filtered_false=list(combined.filter(versions,prereleases=False))
130+
assert"5.3.0b5"notinfiltered_false
131+
assert"4.5.0"infiltered_false
132+
assert"5.0.0"infiltered_false
133+
assert"5.2.0"infiltered_false

‎tests/unit/test_utils.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,11 +328,12 @@ def test_is_valid_url(self):
328328
deftest_download_file(self,tmp_path):
329329
url="https://example.com/test.md"
330330
output=tmp_path/"test_download.md"
331+
expected_content=b"# Test Content\n"
331332

332333
# Mock the requests session to avoid external network calls
333334
mock_response=mock.MagicMock()
334335
mock_response.ok=True
335-
mock_response.content=b"# Test Content\n"
336+
mock_response.content=expected_content
336337

337338
mock_session=mock.MagicMock()
338339
mock_session.get.return_value=mock_response
@@ -341,7 +342,7 @@ def test_download_file(self, tmp_path):
341342
internet.download_file(url,str(output))
342343

343344
assertoutput.exists()
344-
assertoutput.read_bytes()==b"# Test Content\n"
345+
assertoutput.read_bytes()==expected_content
345346

346347
@pytest.mark.utils
347348
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp