Skip to content

Commit

Permalink
add support for PEP 621: basic compatibility with upstream changes in…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
radoering committed Sep 15, 2024
1 parent 88a4327 commit d68e310
Show file tree
Hide file tree
Showing 20 changed files with 205 additions and 118 deletions.
18 changes: 12 additions & 6 deletions src/poetry/console/commands/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,21 +130,27 @@ def handle(self) -> int:

# Load poetry config and display errors, if any
poetry_file = self.poetry.file.path
config = PyProjectTOML(poetry_file).poetry_config
check_result = Factory.validate(config, strict=True)
toml_data = PyProjectTOML(poetry_file).data
check_result = Factory.validate(toml_data, strict=True)

project = toml_data.get("project", {})
poetry_config = toml_data["tool"]["poetry"]

# Validate trove classifiers
project_classifiers = set(config.get("classifiers", []))
project_classifiers = set(
project.get("classifiers") or poetry_config.get("classifiers", [])
)
errors, warnings = self._validate_classifiers(project_classifiers)
check_result["errors"].extend(errors)
check_result["warnings"].extend(warnings)

# Validate readme (files must exist)
if "readme" in config:
errors = self._validate_readme(config["readme"], poetry_file)
# TODO: consider [project.readme] as well
if "readme" in poetry_config:
errors = self._validate_readme(poetry_config["readme"], poetry_file)
check_result["errors"].extend(errors)

check_result["errors"] += self._validate_dependencies_source(config)
check_result["errors"] += self._validate_dependencies_source(poetry_config)

# Verify that lock file is consistent
if self.option("lock") and not self.poetry.locker.is_locked():
Expand Down
8 changes: 6 additions & 2 deletions src/poetry/console/commands/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,12 @@ def handle(self) -> int:

if not self.option("dry-run"):
content: dict[str, Any] = self.poetry.file.read()
poetry_content = content["tool"]["poetry"]
poetry_content["version"] = version.text
project_content = content.get("project", {})
if "version" in project_content:
project_content["version"] = version.text
poetry_content = content.get("tool", {}).get("poetry", {})
if "version" in poetry_content:
poetry_content["version"] = version.text

assert isinstance(content, TOMLDocument)
self.poetry.file.write(content)
Expand Down
20 changes: 12 additions & 8 deletions src/poetry/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def create_poetry(
# Load local sources
repositories = {}
existing_repositories = config.get("repositories", {})
for source in base_poetry.pyproject.poetry_config.get("source", []):
for source in base_poetry.local_config.get("source", []):
name = source.get("name")
url = source.get("url")
if name and url and name not in existing_repositories:
Expand Down Expand Up @@ -340,22 +340,26 @@ def create_pyproject_from_package(cls, package: Package) -> TOMLDocument:

@classmethod
def validate(
cls, config: dict[str, Any], strict: bool = False
cls, toml_data: dict[str, Any], strict: bool = False
) -> dict[str, list[str]]:
results = super().validate(config, strict)
results = super().validate(toml_data, strict)
poetry_config = toml_data["tool"]["poetry"]

results["errors"].extend(validate_object(config))
results["errors"].extend(validate_object(poetry_config))

# A project should not depend on itself.
dependencies = set(config.get("dependencies", {}).keys())
dependencies.update(config.get("dev-dependencies", {}).keys())
groups = config.get("group", {}).values()
# TODO: consider [project.dependencies] and [project.optional-dependencies]
dependencies = set(poetry_config.get("dependencies", {}).keys())
dependencies.update(poetry_config.get("dev-dependencies", {}).keys())
groups = poetry_config.get("group", {}).values()
for group in groups:
dependencies.update(group.get("dependencies", {}).keys())

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

project_name = config.get("name")
project_name = toml_data.get("project", {}).get("name") or poetry_config.get(
"name"
)
if project_name is not None and canonicalize_name(project_name) in dependencies:
results["errors"].append(
f"Project name ({project_name}) is same as one of its dependencies"
Expand Down
70 changes: 57 additions & 13 deletions tests/console/commands/test_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@


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


Expand All @@ -45,9 +45,9 @@ def poetry_with_up_to_date_lockfile(

@pytest.fixture()
def tester(
command_tester_factory: CommandTesterFactory, poetry_sample_project: Poetry
command_tester_factory: CommandTesterFactory, poetry_simple_project: Poetry
) -> CommandTester:
return command_tester_factory("check", poetry=poetry_sample_project)
return command_tester_factory("check", poetry=poetry_simple_project)


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


def test_check_valid_legacy(
mocker: MockerFixture, tester: CommandTester, fixture_dir: FixtureDirGetter
) -> None:
mocker.patch(
"poetry.poetry.Poetry.file",
return_value=TOMLFile(fixture_dir("simple_project_legacy") / "pyproject.toml"),
new_callable=mocker.PropertyMock,
)
tester.execute()

expected = (
"Warning: [tool.poetry.name] is deprecated. Use [project.name] instead.\n"
"Warning: [tool.poetry.version] is set but 'version' is not in "
"[project.dynamic]. If it is static use [project.version]. If it is dynamic, "
"add 'version' to [project.dynamic].\n"
"If you want to set the version dynamically via `poetry build "
"--local-version` or you are using a plugin, which sets the version "
"dynamically, you should define the version in [tool.poetry] and add "
"'version' to [project.dynamic].\n"
"Warning: [tool.poetry.description] is deprecated. Use [project.description] "
"instead.\n"
"Warning: [tool.poetry.readme] is set but 'readme' is not in "
"[project.dynamic]. If it is static use [project.readme]. If it is dynamic, "
"add 'readme' to [project.dynamic].\n"
"If you want to define multiple readmes, you should define them in "
"[tool.poetry] and add 'readme' to [project.dynamic].\n"
"Warning: [tool.poetry.license] is deprecated. Use [project.license] instead.\n"
"Warning: [tool.poetry.authors] is deprecated. Use [project.authors] instead.\n"
"Warning: [tool.poetry.keywords] is deprecated. Use [project.keywords] "
"instead.\n"
"Warning: [tool.poetry.classifiers] is set but 'classifiers' is not in "
"[project.dynamic]. If it is static use [project.classifiers]. If it is "
"dynamic, add 'classifiers' to [project.dynamic].\n"
"ATTENTION: Per default Poetry determines classifiers for supported Python "
"versions and license automatically. If you define classifiers in [project], "
"you disable the automatic enrichment. In other words, you have to define all "
"classifiers manually. If you want to use Poetry's automatic enrichment of "
"classifiers, you should define them in [tool.poetry] and add 'classifiers' "
"to [project.dynamic].\n"
"Warning: [tool.poetry.homepage] is deprecated. Use [project.urls] instead.\n"
"Warning: [tool.poetry.repository] is deprecated. Use [project.urls] instead.\n"
"Warning: [tool.poetry.documentation] is deprecated. Use [project.urls] "
"instead.\n"
"Warning: Defining console scripts in [tool.poetry.scripts] is deprecated. "
"Use [project.scripts] instead. ([tool.poetry.scripts] should only be used "
"for scripts of type 'file').\n"
)

assert tester.io.fetch_error() == expected


def test_check_invalid(
mocker: MockerFixture, tester: CommandTester, fixture_dir: FixtureDirGetter
) -> None:
Expand All @@ -71,10 +122,7 @@ def test_check_invalid(

tester.execute("--lock")

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

assert tester.io.fetch_error() in expected
assert tester.io.fetch_error() == expected


def test_check_private(
Expand Down
12 changes: 6 additions & 6 deletions tests/fixtures/invalid_pyproject/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
[tool.poetry]
[project]
name = "invalid"
version = "1.0.0"
authors = [
"Foo <[email protected]>"
]
readme = "never/exists.md"
license = "INVALID"
license = { text = "INVALID" }
classifiers = [
"Environment :: Console",
"Intended Audience :: Clowns",
"Natural Language :: Ukranian",
"Topic :: Communications :: Chat :: AOL Instant Messenger",
]
dynamic = [ "readme", "dependencies", "requires-python" ]

[tool.poetry]
readme = "never/exists.md"

[tool.poetry.dependencies]
python = "*"
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/no_name_project/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tool.poetry]
name = ""
package-mode = false
version = "1.2.3"
description = "This project has no name"
authors = [
Expand Down
14 changes: 5 additions & 9 deletions tests/fixtures/outdated_lock/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
[tool.poetry]
[project]
name = "foobar"
version = "0.1.0"
description = ""
authors = ["Poetry Developer <[email protected]>"]

[tool.poetry.dependencies]
python = "^3.8"
docker = "4.3.1"

[tool.poetry.group.dev.dependencies]
requires-python = ">=3.8,<4.0"
dependencies = [
"docker>=4.3.1",
]

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
10 changes: 2 additions & 8 deletions tests/fixtures/private_pyproject/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
[tool.poetry]
[project]
name = "private"
version = "0.1.0"
description = ""
authors = ["Your Name <[email protected]>"]
readme = "README.md"
requires-python = ">=3.7,<4.0"
classifiers = [
"Private :: Do Not Upload",
]


[tool.poetry.dependencies]
python = "^3.7"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
24 changes: 12 additions & 12 deletions tests/fixtures/simple_project/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
[tool.poetry]
[project]
name = "simple-project"
version = "1.2.3"
description = "Some description."
authors = [
"Sébastien Eustace <[email protected]>"
{ name = "Sébastien Eustace", email = "[email protected]" }
]
license = "MIT"

readme = ["README.rst"]
license = { text = "MIT" }
readme = "README.rst"
keywords = ["packaging", "dependency", "poetry"]
dynamic = [ "classifiers", "dependencies", "requires-python" ]

[project.urls]
homepage = "https://python-poetry.org"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"

keywords = ["packaging", "dependency", "poetry"]
[project.scripts]
foo = "foo:bar"
baz = "bar:baz.boom.bim"
fox = "fuz.foo:bar.baz"

[tool.poetry]
classifiers = [
"Topic :: Software Development :: Build Tools",
"Topic :: Software Development :: Libraries :: Python Modules"
Expand All @@ -24,12 +30,6 @@ classifiers = [
[tool.poetry.dependencies]
python = "~2.7 || ^3.4"

[tool.poetry.scripts]
foo = "foo:bar"
baz = "bar:baz.boom.bim"
fox = "fuz.foo:bar.baz"


[build-system]
requires = ["poetry-core>=1.1.0a7"]
build-backend = "poetry.core.masonry.api"
2 changes: 2 additions & 0 deletions tests/fixtures/simple_project_legacy/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
My Package
==========
35 changes: 35 additions & 0 deletions tests/fixtures/simple_project_legacy/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[tool.poetry]
name = "simple-project"
version = "1.2.3"
description = "Some description."
authors = [
"Sébastien Eustace <[email protected]>"
]
license = "MIT"

readme = ["README.rst"]

homepage = "https://python-poetry.org"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"

keywords = ["packaging", "dependency", "poetry"]

classifiers = [
"Topic :: Software Development :: Build Tools",
"Topic :: Software Development :: Libraries :: Python Modules"
]

# Requirements
[tool.poetry.dependencies]
python = "~2.7 || ^3.4"

[tool.poetry.scripts]
foo = "foo:bar"
baz = "bar:baz.boom.bim"
fox = "fuz.foo:bar.baz"


[build-system]
requires = ["poetry-core>=1.1.0a7"]
build-backend = "poetry.core.masonry.api"
File renamed without changes.
2 changes: 1 addition & 1 deletion tests/fixtures/up_to_date_lock/poetry.lock

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

14 changes: 5 additions & 9 deletions tests/fixtures/up_to_date_lock/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
[tool.poetry]
[project]
name = "foobar"
version = "0.1.0"
description = ""
authors = ["Poetry Developer <[email protected]>"]

[tool.poetry.dependencies]
python = "^3.8"
docker = ">=4.3.1"

[tool.poetry.group.dev.dependencies]
requires-python = ">=3.8,<4.0"
dependencies = [
"docker>=4.3.1",
]

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
4 changes: 2 additions & 2 deletions tests/installation/test_chef.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def test_prepare_directory(
chef = Chef(
artifact_cache, EnvManager.get_system_env(), Factory.create_pool(config)
)
archive = fixture_dir("simple_project").resolve()
archive = fixture_dir("simple_project_legacy").resolve()

wheel = chef.prepare(archive)

Expand Down Expand Up @@ -111,7 +111,7 @@ def test_prepare_directory_editable(
chef = Chef(
artifact_cache, EnvManager.get_system_env(), Factory.create_pool(config)
)
archive = fixture_dir("simple_project").resolve()
archive = fixture_dir("simple_project_legacy").resolve()

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

Expand Down
Loading

0 comments on commit d68e310

Please sign in to comment.