Skip to content

Commit e425255

Browse files
committed
make allow-prereleases a tri-state setting to really forbid pre-releases if the setting is false and keep default behavior to allow pre-releases only if necessary
1 parent 856e5f4 commit e425255

File tree

7 files changed

+59
-12
lines changed

7 files changed

+59
-12
lines changed

docs/dependency-specification.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,3 +554,17 @@ markers = "platform_python_implementation == 'CPython'"
554554
The same information is still present, and ends up providing the exact
555555
same specification. It's simply split into multiple, slightly more readable,
556556
lines.
557+
558+
### Handling of pre-releases
559+
560+
Per default, Poetry will prefer stable releases and only choose a pre-release
561+
if no stable release satisfies a version constraint. In some cases, this may result in
562+
a solution containing pre-releases even if another solution without pre-releases exists.
563+
564+
If you want to disallow pre-releases for a specific dependency,
565+
you can set `allow-prereleases` to `false`. In this case, dependency resolution will
566+
fail if there is no solution without choosing a pre-release.
567+
568+
If you want to prefer the latest version of a dependency even if it is a pre-release,
569+
you can set `allow-prereleases` to `true` so that Poetry makes no distinction
570+
between stable and pre-release versions during dependency resolution.

src/poetry/console/commands/add.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ def handle(self) -> int:
213213

214214
requirements = self._determine_requirements(
215215
packages,
216-
allow_prereleases=self.option("allow-prereleases"),
216+
allow_prereleases=self.option("allow-prereleases") or None,
217217
source=self.option("source"),
218218
)
219219

src/poetry/console/commands/init.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ def _generate_choice_list(
303303
def _determine_requirements(
304304
self,
305305
requires: list[str],
306-
allow_prereleases: bool = False,
306+
allow_prereleases: bool | None = None,
307307
source: str | None = None,
308308
is_interactive: bool | None = None,
309309
) -> list[dict[str, Any]]:
@@ -440,7 +440,7 @@ def _find_best_version_for_package(
440440
self,
441441
name: str,
442442
required_version: str | None = None,
443-
allow_prereleases: bool = False,
443+
allow_prereleases: bool | None = None,
444444
source: str | None = None,
445445
) -> tuple[str, str]:
446446
from poetry.version.version_selector import VersionSelector

src/poetry/console/commands/show.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ def find_latest_package(
546546
provider = Provider(root, self.poetry.pool, NullIO())
547547
return provider.search_for_direct_origin_dependency(dep)
548548

549-
allow_prereleases = False
549+
allow_prereleases: bool | None = None
550550
for dep in requires:
551551
if dep.name == package.name:
552552
allow_prereleases = dep.allows_prereleases()

src/poetry/repositories/repository.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ def find_packages(self, dependency: Dependency) -> list[Package]:
6060
level="debug",
6161
)
6262

63+
if allow_prereleases is False: # in contrast to None!
64+
return packages
6365
return packages or ignored_pre_release_packages
6466

6567
def has_package(self, package: Package) -> bool:

src/poetry/version/version_selector.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def find_best_candidate(
1717
self,
1818
package_name: str,
1919
target_package_version: str | None = None,
20-
allow_prereleases: bool = False,
20+
allow_prereleases: bool | None = None,
2121
source: str | None = None,
2222
) -> Package | None:
2323
"""

tests/repositories/test_repository.py

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,46 @@
1010

1111

1212
@pytest.fixture(scope="module")
13-
def black_repository() -> Repository:
13+
def repository() -> Repository:
1414
repo = Repository("repo")
15+
16+
# latest version pre-release
17+
repo.add_package(get_package("foo", "1.0"))
18+
repo.add_package(get_package("foo", "2.0.b0"))
19+
20+
# latest version yanked
1521
repo.add_package(get_package("black", "19.10b0"))
1622
repo.add_package(get_package("black", "21.11b0", yanked="reason"))
23+
1724
return repo
1825

1926

27+
@pytest.mark.parametrize(
28+
("allow_prereleases", "constraint", "expected"),
29+
[
30+
(None, ">=1.0", ["1.0"]),
31+
(False, ">=1.0", ["1.0"]),
32+
(True, ">=1.0", ["1.0", "2.0.b0"]),
33+
(None, ">=1.5", ["2.0.b0"]),
34+
(False, ">=1.5", []),
35+
(True, ">=1.5", ["2.0.b0"]),
36+
],
37+
)
38+
def test_find_packages_allow_prereleases(
39+
repository: Repository,
40+
allow_prereleases: bool | None,
41+
constraint: str,
42+
expected: list[str],
43+
) -> None:
44+
packages = repository.find_packages(
45+
Factory.create_dependency(
46+
"foo", {"version": constraint, "allow-prereleases": allow_prereleases}
47+
)
48+
)
49+
50+
assert [str(p.version) for p in packages] == expected
51+
52+
2053
@pytest.mark.parametrize(
2154
["constraint", "expected"],
2255
[
@@ -29,11 +62,9 @@ def black_repository() -> Repository:
2962
],
3063
)
3164
def test_find_packages_yanked(
32-
black_repository: Repository, constraint: str, expected: list[str]
65+
repository: Repository, constraint: str, expected: list[str]
3366
) -> None:
34-
packages = black_repository.find_packages(
35-
Factory.create_dependency("black", constraint)
36-
)
67+
packages = repository.find_packages(Factory.create_dependency("black", constraint))
3768

3869
assert [str(p.version) for p in packages] == expected
3970

@@ -46,13 +77,13 @@ def test_find_packages_yanked(
4677
],
4778
)
4879
def test_package_yanked(
49-
black_repository: Repository,
80+
repository: Repository,
5081
package_name: str,
5182
version: str,
5283
yanked: bool,
5384
yanked_reason: str,
5485
) -> None:
55-
package = black_repository.package(package_name, Version.parse(version))
86+
package = repository.package(package_name, Version.parse(version))
5687

5788
assert package.name == package_name
5889
assert str(package.version) == version

0 commit comments

Comments
 (0)