Skip to content

Commit d68e310

Browse files
committed
add support for PEP 621: basic compatibility with upstream changes in poetry-core (#9135)
- update some pyproject.toml files to avoid deprecation warnings in tests for `poetry check` - add explicit test for deprecation warnings that should be printed when running `poetry check` with a legacy project
1 parent 88a4327 commit d68e310

File tree

20 files changed

+205
-118
lines changed

20 files changed

+205
-118
lines changed

src/poetry/console/commands/check.py

+12-6
Original file line numberDiff line numberDiff line change
@@ -130,21 +130,27 @@ def handle(self) -> int:
130130

131131
# Load poetry config and display errors, if any
132132
poetry_file = self.poetry.file.path
133-
config = PyProjectTOML(poetry_file).poetry_config
134-
check_result = Factory.validate(config, strict=True)
133+
toml_data = PyProjectTOML(poetry_file).data
134+
check_result = Factory.validate(toml_data, strict=True)
135+
136+
project = toml_data.get("project", {})
137+
poetry_config = toml_data["tool"]["poetry"]
135138

136139
# Validate trove classifiers
137-
project_classifiers = set(config.get("classifiers", []))
140+
project_classifiers = set(
141+
project.get("classifiers") or poetry_config.get("classifiers", [])
142+
)
138143
errors, warnings = self._validate_classifiers(project_classifiers)
139144
check_result["errors"].extend(errors)
140145
check_result["warnings"].extend(warnings)
141146

142147
# Validate readme (files must exist)
143-
if "readme" in config:
144-
errors = self._validate_readme(config["readme"], poetry_file)
148+
# TODO: consider [project.readme] as well
149+
if "readme" in poetry_config:
150+
errors = self._validate_readme(poetry_config["readme"], poetry_file)
145151
check_result["errors"].extend(errors)
146152

147-
check_result["errors"] += self._validate_dependencies_source(config)
153+
check_result["errors"] += self._validate_dependencies_source(poetry_config)
148154

149155
# Verify that lock file is consistent
150156
if self.option("lock") and not self.poetry.locker.is_locked():

src/poetry/console/commands/version.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,12 @@ def handle(self) -> int:
6969

7070
if not self.option("dry-run"):
7171
content: dict[str, Any] = self.poetry.file.read()
72-
poetry_content = content["tool"]["poetry"]
73-
poetry_content["version"] = version.text
72+
project_content = content.get("project", {})
73+
if "version" in project_content:
74+
project_content["version"] = version.text
75+
poetry_content = content.get("tool", {}).get("poetry", {})
76+
if "version" in poetry_content:
77+
poetry_content["version"] = version.text
7478

7579
assert isinstance(content, TOMLDocument)
7680
self.poetry.file.write(content)

src/poetry/factory.py

+12-8
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def create_poetry(
7373
# Load local sources
7474
repositories = {}
7575
existing_repositories = config.get("repositories", {})
76-
for source in base_poetry.pyproject.poetry_config.get("source", []):
76+
for source in base_poetry.local_config.get("source", []):
7777
name = source.get("name")
7878
url = source.get("url")
7979
if name and url and name not in existing_repositories:
@@ -340,22 +340,26 @@ def create_pyproject_from_package(cls, package: Package) -> TOMLDocument:
340340

341341
@classmethod
342342
def validate(
343-
cls, config: dict[str, Any], strict: bool = False
343+
cls, toml_data: dict[str, Any], strict: bool = False
344344
) -> dict[str, list[str]]:
345-
results = super().validate(config, strict)
345+
results = super().validate(toml_data, strict)
346+
poetry_config = toml_data["tool"]["poetry"]
346347

347-
results["errors"].extend(validate_object(config))
348+
results["errors"].extend(validate_object(poetry_config))
348349

349350
# A project should not depend on itself.
350-
dependencies = set(config.get("dependencies", {}).keys())
351-
dependencies.update(config.get("dev-dependencies", {}).keys())
352-
groups = config.get("group", {}).values()
351+
# TODO: consider [project.dependencies] and [project.optional-dependencies]
352+
dependencies = set(poetry_config.get("dependencies", {}).keys())
353+
dependencies.update(poetry_config.get("dev-dependencies", {}).keys())
354+
groups = poetry_config.get("group", {}).values()
353355
for group in groups:
354356
dependencies.update(group.get("dependencies", {}).keys())
355357

356358
dependencies = {canonicalize_name(d) for d in dependencies}
357359

358-
project_name = config.get("name")
360+
project_name = toml_data.get("project", {}).get("name") or poetry_config.get(
361+
"name"
362+
)
359363
if project_name is not None and canonicalize_name(project_name) in dependencies:
360364
results["errors"].append(
361365
f"Project name ({project_name}) is same as one of its dependencies"

tests/console/commands/test_check.py

+57-13
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222

2323

2424
@pytest.fixture
25-
def poetry_sample_project(set_project_context: SetProjectContext) -> Iterator[Poetry]:
26-
with set_project_context("sample_project", in_place=False) as cwd:
25+
def poetry_simple_project(set_project_context: SetProjectContext) -> Iterator[Poetry]:
26+
with set_project_context("simple_project", in_place=False) as cwd:
2727
yield Factory().create_poetry(cwd)
2828

2929

@@ -45,9 +45,9 @@ def poetry_with_up_to_date_lockfile(
4545

4646
@pytest.fixture()
4747
def tester(
48-
command_tester_factory: CommandTesterFactory, poetry_sample_project: Poetry
48+
command_tester_factory: CommandTesterFactory, poetry_simple_project: Poetry
4949
) -> CommandTester:
50-
return command_tester_factory("check", poetry=poetry_sample_project)
50+
return command_tester_factory("check", poetry=poetry_simple_project)
5151

5252

5353
def test_check_valid(tester: CommandTester) -> None:
@@ -60,6 +60,57 @@ def test_check_valid(tester: CommandTester) -> None:
6060
assert tester.io.fetch_output() == expected
6161

6262

63+
def test_check_valid_legacy(
64+
mocker: MockerFixture, tester: CommandTester, fixture_dir: FixtureDirGetter
65+
) -> None:
66+
mocker.patch(
67+
"poetry.poetry.Poetry.file",
68+
return_value=TOMLFile(fixture_dir("simple_project_legacy") / "pyproject.toml"),
69+
new_callable=mocker.PropertyMock,
70+
)
71+
tester.execute()
72+
73+
expected = (
74+
"Warning: [tool.poetry.name] is deprecated. Use [project.name] instead.\n"
75+
"Warning: [tool.poetry.version] is set but 'version' is not in "
76+
"[project.dynamic]. If it is static use [project.version]. If it is dynamic, "
77+
"add 'version' to [project.dynamic].\n"
78+
"If you want to set the version dynamically via `poetry build "
79+
"--local-version` or you are using a plugin, which sets the version "
80+
"dynamically, you should define the version in [tool.poetry] and add "
81+
"'version' to [project.dynamic].\n"
82+
"Warning: [tool.poetry.description] is deprecated. Use [project.description] "
83+
"instead.\n"
84+
"Warning: [tool.poetry.readme] is set but 'readme' is not in "
85+
"[project.dynamic]. If it is static use [project.readme]. If it is dynamic, "
86+
"add 'readme' to [project.dynamic].\n"
87+
"If you want to define multiple readmes, you should define them in "
88+
"[tool.poetry] and add 'readme' to [project.dynamic].\n"
89+
"Warning: [tool.poetry.license] is deprecated. Use [project.license] instead.\n"
90+
"Warning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n"
91+
"Warning: [tool.poetry.keywords] is deprecated. Use [project.keywords] "
92+
"instead.\n"
93+
"Warning: [tool.poetry.classifiers] is set but 'classifiers' is not in "
94+
"[project.dynamic]. If it is static use [project.classifiers]. If it is "
95+
"dynamic, add 'classifiers' to [project.dynamic].\n"
96+
"ATTENTION: Per default Poetry determines classifiers for supported Python "
97+
"versions and license automatically. If you define classifiers in [project], "
98+
"you disable the automatic enrichment. In other words, you have to define all "
99+
"classifiers manually. If you want to use Poetry's automatic enrichment of "
100+
"classifiers, you should define them in [tool.poetry] and add 'classifiers' "
101+
"to [project.dynamic].\n"
102+
"Warning: [tool.poetry.homepage] is deprecated. Use [project.urls] instead.\n"
103+
"Warning: [tool.poetry.repository] is deprecated. Use [project.urls] instead.\n"
104+
"Warning: [tool.poetry.documentation] is deprecated. Use [project.urls] "
105+
"instead.\n"
106+
"Warning: Defining console scripts in [tool.poetry.scripts] is deprecated. "
107+
"Use [project.scripts] instead. ([tool.poetry.scripts] should only be used "
108+
"for scripts of type 'file').\n"
109+
)
110+
111+
assert tester.io.fetch_error() == expected
112+
113+
63114
def test_check_invalid(
64115
mocker: MockerFixture, tester: CommandTester, fixture_dir: FixtureDirGetter
65116
) -> None:
@@ -71,10 +122,7 @@ def test_check_invalid(
71122

72123
tester.execute("--lock")
73124

74-
fastjsonschema_error = "data must contain ['description'] properties"
75-
custom_error = "The fields ['description'] are required in package mode."
76-
expected_template = """\
77-
Error: {schema_error}
125+
expected = """\
78126
Error: Project name (invalid) is same as one of its dependencies
79127
Error: Unrecognized classifiers: ['Intended Audience :: Clowns'].
80128
Error: Declared README file does not exist: never/exists.md
@@ -91,12 +139,8 @@ def test_check_invalid(
91139
'Topic :: Communications :: Chat :: AOL Instant Messenger'.\
92140
Must be removed.
93141
"""
94-
expected = {
95-
expected_template.format(schema_error=schema_error)
96-
for schema_error in (fastjsonschema_error, custom_error)
97-
}
98142

99-
assert tester.io.fetch_error() in expected
143+
assert tester.io.fetch_error() == expected
100144

101145

102146
def test_check_private(

tests/fixtures/invalid_pyproject/pyproject.toml

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
[tool.poetry]
1+
[project]
22
name = "invalid"
33
version = "1.0.0"
4-
authors = [
5-
6-
]
7-
readme = "never/exists.md"
8-
license = "INVALID"
4+
license = { text = "INVALID" }
95
classifiers = [
106
"Environment :: Console",
117
"Intended Audience :: Clowns",
128
"Natural Language :: Ukranian",
139
"Topic :: Communications :: Chat :: AOL Instant Messenger",
1410
]
11+
dynamic = [ "readme", "dependencies", "requires-python" ]
12+
13+
[tool.poetry]
14+
readme = "never/exists.md"
1515

1616
[tool.poetry.dependencies]
1717
python = "*"

tests/fixtures/no_name_project/pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[tool.poetry]
2-
name = ""
2+
package-mode = false
33
version = "1.2.3"
44
description = "This project has no name"
55
authors = [

tests/fixtures/outdated_lock/pyproject.toml

+5-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
[tool.poetry]
1+
[project]
22
name = "foobar"
33
version = "0.1.0"
4-
description = ""
5-
authors = ["Poetry Developer <[email protected]>"]
6-
7-
[tool.poetry.dependencies]
8-
python = "^3.8"
9-
docker = "4.3.1"
10-
11-
[tool.poetry.group.dev.dependencies]
4+
requires-python = ">=3.8,<4.0"
5+
dependencies = [
6+
"docker>=4.3.1",
7+
]
128

139
[build-system]
1410
requires = ["poetry-core>=1.0.0"]
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
1-
[tool.poetry]
1+
[project]
22
name = "private"
33
version = "0.1.0"
4-
description = ""
5-
authors = ["Your Name <[email protected]>"]
6-
readme = "README.md"
4+
requires-python = ">=3.7,<4.0"
75
classifiers = [
86
"Private :: Do Not Upload",
97
]
108

11-
12-
[tool.poetry.dependencies]
13-
python = "^3.7"
14-
159
[build-system]
1610
requires = ["poetry-core"]
1711
build-backend = "poetry.core.masonry.api"
+12-12
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
1-
[tool.poetry]
1+
[project]
22
name = "simple-project"
33
version = "1.2.3"
44
description = "Some description."
55
authors = [
6-
"Sébastien Eustace <[email protected]>"
6+
{ name = "Sébastien Eustace", email = "[email protected]" }
77
]
8-
license = "MIT"
9-
10-
readme = ["README.rst"]
8+
license = { text = "MIT" }
9+
readme = "README.rst"
10+
keywords = ["packaging", "dependency", "poetry"]
11+
dynamic = [ "classifiers", "dependencies", "requires-python" ]
1112

13+
[project.urls]
1214
homepage = "https://python-poetry.org"
1315
repository = "https://github.com/python-poetry/poetry"
1416
documentation = "https://python-poetry.org/docs"
1517

16-
keywords = ["packaging", "dependency", "poetry"]
18+
[project.scripts]
19+
foo = "foo:bar"
20+
baz = "bar:baz.boom.bim"
21+
fox = "fuz.foo:bar.baz"
1722

23+
[tool.poetry]
1824
classifiers = [
1925
"Topic :: Software Development :: Build Tools",
2026
"Topic :: Software Development :: Libraries :: Python Modules"
@@ -24,12 +30,6 @@ classifiers = [
2430
[tool.poetry.dependencies]
2531
python = "~2.7 || ^3.4"
2632

27-
[tool.poetry.scripts]
28-
foo = "foo:bar"
29-
baz = "bar:baz.boom.bim"
30-
fox = "fuz.foo:bar.baz"
31-
32-
3333
[build-system]
3434
requires = ["poetry-core>=1.1.0a7"]
3535
build-backend = "poetry.core.masonry.api"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
My Package
2+
==========
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[tool.poetry]
2+
name = "simple-project"
3+
version = "1.2.3"
4+
description = "Some description."
5+
authors = [
6+
"Sébastien Eustace <[email protected]>"
7+
]
8+
license = "MIT"
9+
10+
readme = ["README.rst"]
11+
12+
homepage = "https://python-poetry.org"
13+
repository = "https://github.com/python-poetry/poetry"
14+
documentation = "https://python-poetry.org/docs"
15+
16+
keywords = ["packaging", "dependency", "poetry"]
17+
18+
classifiers = [
19+
"Topic :: Software Development :: Build Tools",
20+
"Topic :: Software Development :: Libraries :: Python Modules"
21+
]
22+
23+
# Requirements
24+
[tool.poetry.dependencies]
25+
python = "~2.7 || ^3.4"
26+
27+
[tool.poetry.scripts]
28+
foo = "foo:bar"
29+
baz = "bar:baz.boom.bim"
30+
fox = "fuz.foo:bar.baz"
31+
32+
33+
[build-system]
34+
requires = ["poetry-core>=1.1.0a7"]
35+
build-backend = "poetry.core.masonry.api"

tests/fixtures/up_to_date_lock/poetry.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/fixtures/up_to_date_lock/pyproject.toml

+5-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
[tool.poetry]
1+
[project]
22
name = "foobar"
33
version = "0.1.0"
4-
description = ""
5-
authors = ["Poetry Developer <[email protected]>"]
6-
7-
[tool.poetry.dependencies]
8-
python = "^3.8"
9-
docker = ">=4.3.1"
10-
11-
[tool.poetry.group.dev.dependencies]
4+
requires-python = ">=3.8,<4.0"
5+
dependencies = [
6+
"docker>=4.3.1",
7+
]
128

139
[build-system]
1410
requires = ["poetry-core>=1.0.0"]

tests/installation/test_chef.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def test_prepare_directory(
6969
chef = Chef(
7070
artifact_cache, EnvManager.get_system_env(), Factory.create_pool(config)
7171
)
72-
archive = fixture_dir("simple_project").resolve()
72+
archive = fixture_dir("simple_project_legacy").resolve()
7373

7474
wheel = chef.prepare(archive)
7575

@@ -111,7 +111,7 @@ def test_prepare_directory_editable(
111111
chef = Chef(
112112
artifact_cache, EnvManager.get_system_env(), Factory.create_pool(config)
113113
)
114-
archive = fixture_dir("simple_project").resolve()
114+
archive = fixture_dir("simple_project_legacy").resolve()
115115

116116
wheel = chef.prepare(archive, editable=True)
117117

0 commit comments

Comments
 (0)