Skip to content

Commit

Permalink
make allow-prereleases a tri-state setting to really forbid pre-rel…
Browse files Browse the repository at this point in the history
…eases if the setting is `false` and keep default behavior to allow pre-releases only if necessary
  • Loading branch information
radoering committed Nov 17, 2024
1 parent 6e1bf8b commit 380dc09
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 13 deletions.
14 changes: 14 additions & 0 deletions docs/dependency-specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -554,3 +554,17 @@ markers = "platform_python_implementation == 'CPython'"
The same information is still present, and ends up providing the exact
same specification. It's simply split into multiple, slightly more readable,
lines.

### Handling of pre-releases

Per default, Poetry will prefer stable releases and only choose a pre-release
if no stable release satisfies a version constraint. In some cases, this may result in
a solution containing pre-releases even if another solution without pre-releases exists.

If you want to disallow pre-releases for a specific dependency,
you can set `allow-prereleases` to `false`. In this case, dependency resolution will
fail if there is no solution without choosing a pre-release.

If you want to prefer the latest version of a dependency even if it is a pre-release,
you can set `allow-prereleases` to `true` so that Poetry makes no distinction
between stable and pre-release versions during dependency resolution.
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/poetry/console/commands/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ def handle(self) -> int:

requirements = self._determine_requirements(
packages,
allow_prereleases=self.option("allow-prereleases"),
allow_prereleases=self.option("allow-prereleases") or None,
source=self.option("source"),
)

Expand Down
4 changes: 2 additions & 2 deletions src/poetry/console/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ def _generate_choice_list(
def _determine_requirements(
self,
requires: list[str],
allow_prereleases: bool = False,
allow_prereleases: bool | None = None,
source: str | None = None,
is_interactive: bool | None = None,
) -> list[dict[str, Any]]:
Expand Down Expand Up @@ -440,7 +440,7 @@ def _find_best_version_for_package(
self,
name: str,
required_version: str | None = None,
allow_prereleases: bool = False,
allow_prereleases: bool | None = None,
source: str | None = None,
) -> tuple[str, str]:
from poetry.version.version_selector import VersionSelector
Expand Down
2 changes: 1 addition & 1 deletion src/poetry/console/commands/show.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ def find_latest_package(
provider = Provider(root, self.poetry.pool, NullIO())
return provider.search_for_direct_origin_dependency(dep)

allow_prereleases = False
allow_prereleases: bool | None = None
for dep in requires:
if dep.name == package.name:
allow_prereleases = dep.allows_prereleases()
Expand Down
2 changes: 2 additions & 0 deletions src/poetry/repositories/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ def find_packages(self, dependency: Dependency) -> list[Package]:
level="debug",
)

if allow_prereleases is False: # in contrast to None!
return packages
return packages or ignored_pre_release_packages

def has_package(self, package: Package) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion src/poetry/version/version_selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def find_best_candidate(
self,
package_name: str,
target_package_version: str | None = None,
allow_prereleases: bool = False,
allow_prereleases: bool | None = None,
source: str | None = None,
) -> Package | None:
"""
Expand Down
45 changes: 38 additions & 7 deletions tests/repositories/test_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,46 @@


@pytest.fixture(scope="module")
def black_repository() -> Repository:
def repository() -> Repository:
repo = Repository("repo")

# latest version pre-release
repo.add_package(get_package("foo", "1.0"))
repo.add_package(get_package("foo", "2.0.b0"))

# latest version yanked
repo.add_package(get_package("black", "19.10b0"))
repo.add_package(get_package("black", "21.11b0", yanked="reason"))

return repo


@pytest.mark.parametrize(
("allow_prereleases", "constraint", "expected"),
[
(None, ">=1.0", ["1.0"]),
(False, ">=1.0", ["1.0"]),
(True, ">=1.0", ["1.0", "2.0.b0"]),
(None, ">=1.5", ["2.0.b0"]),
(False, ">=1.5", []),
(True, ">=1.5", ["2.0.b0"]),
],
)
def test_find_packages_allow_prereleases(
repository: Repository,
allow_prereleases: bool | None,
constraint: str,
expected: list[str],
) -> None:
packages = repository.find_packages(
Factory.create_dependency(
"foo", {"version": constraint, "allow-prereleases": allow_prereleases}
)
)

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


@pytest.mark.parametrize(
["constraint", "expected"],
[
Expand All @@ -29,11 +62,9 @@ def black_repository() -> Repository:
],
)
def test_find_packages_yanked(
black_repository: Repository, constraint: str, expected: list[str]
repository: Repository, constraint: str, expected: list[str]
) -> None:
packages = black_repository.find_packages(
Factory.create_dependency("black", constraint)
)
packages = repository.find_packages(Factory.create_dependency("black", constraint))

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

Expand All @@ -46,13 +77,13 @@ def test_find_packages_yanked(
],
)
def test_package_yanked(
black_repository: Repository,
repository: Repository,
package_name: str,
version: str,
yanked: bool,
yanked_reason: str,
) -> None:
package = black_repository.package(package_name, Version.parse(version))
package = repository.package(package_name, Version.parse(version))

assert package.name == package_name
assert str(package.version) == version
Expand Down

0 comments on commit 380dc09

Please sign in to comment.