Skip to content

Commit

Permalink
introduce tool.poetry.requires-poetry to define a constraint for th…
Browse files Browse the repository at this point in the history
…e Poetry version the project is compatible with (python-poetry#9547)
  • Loading branch information
radoering authored and Secrus committed Oct 27, 2024
1 parent 17d0a3a commit 0a37679
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 0 deletions.
11 changes: 11 additions & 0 deletions docs/pyproject.md
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,17 @@ any custom url in the `urls` section.

If you publish your package on PyPI, they will appear in the `Project Links` section.

## `requires-poetry`

A constraint for the Poetry version that is required for this project.
If you are using a Poetry version that is not allowed by this constraint,
an error will be raised.

```toml
[tool.poetry]
requires-poetry = ">=2.0"
```

## `requires-plugins`

In this section, you can specify that certain plugins are required for your project:
Expand Down
12 changes: 12 additions & 0 deletions src/poetry/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@

from cleo.io.null_io import NullIO
from packaging.utils import canonicalize_name
from poetry.core.constraints.version import Version
from poetry.core.constraints.version import parse_constraint
from poetry.core.factory import Factory as BaseFactory
from poetry.core.packages.dependency_group import MAIN_GROUP

from poetry.__version__ import __version__
from poetry.config.config import Config
from poetry.exceptions import PoetryError
from poetry.json import validate_object
Expand Down Expand Up @@ -56,6 +59,15 @@ def create_poetry(

base_poetry = super().create_poetry(cwd=cwd, with_groups=with_groups)

if version_str := base_poetry.local_config.get("requires-poetry"):
version_constraint = parse_constraint(version_str)
version = Version.parse(__version__)
if not version_constraint.allows(version):
raise PoetryError(
f"This project requires Poetry {version_constraint},"
f" but you are using Poetry {version}"
)

poetry_file = base_poetry.pyproject_path
locker = Locker(poetry_file.parent / "poetry.lock", base_poetry.pyproject.data)

Expand Down
5 changes: 5 additions & 0 deletions src/poetry/json/schemas/poetry.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"type": "object",
"required": [],
"properties": {
"requires-poetry": {
"type": "string",
"description": "The version constraint for Poetry itself.",
"$ref": "#/definitions/dependency"
},
"requires-plugins": {
"type": "object",
"description": "Poetry plugins that are required for this project.",
Expand Down
6 changes: 6 additions & 0 deletions tests/fixtures/self_version_not_ok/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[tool.poetry]
package-mode = false
requires-poetry = "<1.2"

[tool.poetry.dependencies]
python = "^3.8"
6 changes: 6 additions & 0 deletions tests/fixtures/self_version_ok/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[tool.poetry]
package-mode = false
requires-poetry = ">=1.2"

[tool.poetry.dependencies]
python = "^3.8"
6 changes: 6 additions & 0 deletions tests/json/fixtures/self_invalid_version.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[tool.poetry]
name = "foobar"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
requires-poetry = 2
1 change: 1 addition & 0 deletions tests/json/fixtures/self_valid.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name = "foobar"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
requires-poetry = ">=2.0"

[tool.poetry.requires-plugins]
foo = ">=1.0"
8 changes: 8 additions & 0 deletions tests/json/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ def test_self_valid() -> None:
assert Factory.validate(toml) == {"errors": [], "warnings": []}


def test_self_invalid_version() -> None:
toml: dict[str, Any] = TOMLFile(FIXTURE_DIR / "self_invalid_version.toml").read()
assert Factory.validate(toml) == {
"errors": ["data.requires-poetry must be string"],
"warnings": [],
}


def test_self_invalid_plugin() -> None:
toml: dict[str, Any] = TOMLFile(FIXTURE_DIR / "self_invalid_plugin.toml").read()
assert Factory.validate(toml) == {
Expand Down
18 changes: 18 additions & 0 deletions tests/test_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from poetry.core.packages.package import Package
from poetry.core.packages.vcs_dependency import VCSDependency

from poetry.__version__ import __version__
from poetry.exceptions import PoetryError
from poetry.factory import Factory
from poetry.plugins.plugin import Plugin
Expand Down Expand Up @@ -230,6 +231,23 @@ def test_create_poetry_non_package_mode(fixture_dir: FixtureDirGetter) -> None:
assert not poetry.is_package_mode


def test_create_poetry_version_ok(fixture_dir: FixtureDirGetter) -> None:
io = BufferedIO()
Factory().create_poetry(fixture_dir("self_version_ok"), io=io)

assert io.fetch_output() == ""
assert io.fetch_error() == ""


def test_create_poetry_version_not_ok(fixture_dir: FixtureDirGetter) -> None:
with pytest.raises(PoetryError) as e:
Factory().create_poetry(fixture_dir("self_version_not_ok"))
assert (
str(e.value)
== f"This project requires Poetry <1.2, but you are using Poetry {__version__}"
)


@pytest.mark.parametrize(
"project",
("with_primary_source_implicit", "with_primary_source_explicit"),
Expand Down

0 comments on commit 0a37679

Please sign in to comment.