Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moving "poetry lock --check" to "poetry check --lock" #8015

Merged
merged 17 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -625,8 +625,9 @@ As such, `exit` should be used to properly exit the shell and the virtual enviro

## check

The `check` command validates the structure of the `pyproject.toml` file
and returns a detailed report if there are any errors.
The `check` command validates the content of the `pyproject.toml` file
and its consistency with the `poetry.lock` file.
It returns a detailed report if there are any errors.

{{% note %}}
This command is also available as a pre-commit hook. See [pre-commit hooks]({{< relref "pre-commit-hooks#poetry-check">}}) for more information.
Expand All @@ -636,6 +637,10 @@ This command is also available as a pre-commit hook. See [pre-commit hooks]({{<
poetry check
```

### Options

* `--lock`: Verifies that `poetry.lock` exists for the current `pyproject.toml`.
samypr100 marked this conversation as resolved.
Show resolved Hide resolved

## search

This command searches for packages on a remote index.
Expand All @@ -659,7 +664,7 @@ poetry lock

### Options

* `--check`: Verify that `poetry.lock` is consistent with `pyproject.toml`
* `--check`: Verify that `poetry.lock` is consistent with `pyproject.toml`. (**Deprecated**) Use `poetry check --lock` instead.
* `--no-update`: Do not update locked versions, only refresh lock file.

## version
Expand Down Expand Up @@ -944,7 +949,7 @@ poetry self lock

#### Options

* `--check`: Verify that `poetry.lock` is consistent with `pyproject.toml`
* `--check`: Verify that `poetry.lock` is consistent with `pyproject.toml`. (**Deprecated**)
* `--no-update`: Do not update locked versions, only refresh lock file.

### self show
Expand Down
27 changes: 26 additions & 1 deletion src/poetry/console/commands/check.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
from __future__ import annotations

from cleo.helpers import option

from poetry.console.commands.command import Command


class CheckCommand(Command):
name = "check"
description = "Checks the validity of the <comment>pyproject.toml</comment> file."
description = (
"Validates the content of the <comment>pyproject.toml</> file and its"
" consistency with the poetry.lock file."
)

options = [
option(
"lock",
None,
(
"Checks that <comment>poetry.lock</> exists for the current"
" version of <comment>pyproject.toml</>."
),
),
]

def validate_classifiers(
self, project_classifiers: set[str]
Expand Down Expand Up @@ -72,6 +88,15 @@ def handle(self) -> int:
check_result["errors"].extend(errors)
check_result["warnings"].extend(warnings)

# Verify that lock file is consistent
if self.option("lock") and not self.poetry.locker.is_locked():
check_result["errors"] += ["poetry.lock was not found."]
if self.poetry.locker.is_locked() and not self.poetry.locker.is_fresh():
check_result["errors"] += [
"poetry.lock is not consistent with pyproject.toml. Run `poetry"
" lock [--no-update]` to fix it."
]

if not check_result["errors"] and not check_result["warnings"]:
self.info("All set!")

Expand Down
7 changes: 6 additions & 1 deletion src/poetry/console/commands/lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class LockCommand(InstallerCommand):
None,
(
"Check that the <comment>poetry.lock</> file corresponds to the current"
" version of <comment>pyproject.toml</>."
" version of <comment>pyproject.toml</>. (<warning>Deprecated</>) Use"
" <comment>poetry check --lock</> instead."
),
),
]
Expand All @@ -36,6 +37,10 @@ class LockCommand(InstallerCommand):

def handle(self) -> int:
if self.option("check"):
self.line_error(
"<warning>poetry lock --check is deprecated, use `poetry"
" check --lock` instead.</warning>"
)
if self.poetry.locker.is_locked() and self.poetry.locker.is_fresh():
self.line("poetry.lock is consistent with pyproject.toml.")
return 0
Expand Down
124 changes: 123 additions & 1 deletion tests/console/commands/test_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,56 @@

import pytest

from poetry.packages import Locker


if TYPE_CHECKING:
import httpretty

from cleo.testers.command_tester import CommandTester
from pytest_mock import MockerFixture

from poetry.poetry import Poetry
from tests.types import CommandTesterFactory
from tests.types import FixtureDirGetter
from tests.types import ProjectFactory


@pytest.fixture()
def tester(command_tester_factory: CommandTesterFactory) -> CommandTester:
return command_tester_factory("check")


def _project_factory(
fixture_name: str,
project_factory: ProjectFactory,
fixture_dir: FixtureDirGetter,
) -> Poetry:
source = fixture_dir(fixture_name)
pyproject_content = (source / "pyproject.toml").read_text(encoding="utf-8")
poetry_lock_content = (source / "poetry.lock").read_text(encoding="utf-8")
return project_factory(
name="foobar",
pyproject_content=pyproject_content,
poetry_lock_content=poetry_lock_content,
source=source,
)


@pytest.fixture
def poetry_with_outdated_lockfile(
project_factory: ProjectFactory, fixture_dir: FixtureDirGetter
) -> Poetry:
return _project_factory("outdated_lock", project_factory, fixture_dir)


@pytest.fixture
def poetry_with_up_to_date_lockfile(
project_factory: ProjectFactory, fixture_dir: FixtureDirGetter
) -> Poetry:
return _project_factory("up_to_date_lock", project_factory, fixture_dir)


def test_check_valid(tester: CommandTester) -> None:
tester.execute()

Expand All @@ -39,12 +75,13 @@ def test_check_invalid(
new_callable=mocker.PropertyMock,
)

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

expected = """\
Error: 'description' is a required property
Error: Project name (invalid) is same as one of its dependencies
Error: Unrecognized classifiers: ['Intended Audience :: Clowns'].
Error: poetry.lock was not found.
Warning: A wildcard Python dependency is ambiguous.\
Consider specifying a more explicit one.
Warning: The "pendulum" dependency specifies the "allows-prereleases" property,\
Expand Down Expand Up @@ -74,3 +111,88 @@ def test_check_private(
"""

assert tester.io.fetch_output() == expected


@pytest.mark.parametrize(
("options", "expected", "expected_status"),
[
("", "All set!\n", 0),
("--lock", "Error: poetry.lock was not found.\n", 1),
],
)
def test_check_lock_missing(
mocker: MockerFixture,
tester: CommandTester,
fixture_dir: FixtureDirGetter,
options: str,
expected: str,
expected_status: int,
) -> None:
from poetry.toml import TOMLFile

mocker.patch(
"poetry.poetry.Poetry.file",
return_value=TOMLFile(fixture_dir("private_pyproject") / "pyproject.toml"),
new_callable=mocker.PropertyMock,
)

status_code = tester.execute(options)

assert status_code == expected_status

if status_code == 0:
assert tester.io.fetch_output() == expected
else:
assert tester.io.fetch_error() == expected


@pytest.mark.parametrize("options", ["", "--lock"])
def test_check_lock_outdated(
command_tester_factory: CommandTesterFactory,
poetry_with_outdated_lockfile: Poetry,
http: type[httpretty.httpretty],
options: str,
) -> None:
http.disable()

locker = Locker(
lock=poetry_with_outdated_lockfile.pyproject.file.path.parent / "poetry.lock",
local_config=poetry_with_outdated_lockfile.locker._local_config,
)
poetry_with_outdated_lockfile.set_locker(locker)

tester = command_tester_factory("check", poetry=poetry_with_outdated_lockfile)
status_code = tester.execute(options)
expected = (
"Error: poetry.lock is not consistent with pyproject.toml. "
"Run `poetry lock [--no-update]` to fix it.\n"
)

assert tester.io.fetch_error() == expected

# exit with an error
assert status_code == 1


@pytest.mark.parametrize("options", ["", "--lock"])
def test_check_lock_up_to_date(
command_tester_factory: CommandTesterFactory,
poetry_with_up_to_date_lockfile: Poetry,
http: type[httpretty.httpretty],
options: str,
) -> None:
http.disable()

locker = Locker(
lock=poetry_with_up_to_date_lockfile.pyproject.file.path.parent / "poetry.lock",
local_config=poetry_with_up_to_date_lockfile.locker._local_config,
)
poetry_with_up_to_date_lockfile.set_locker(locker)

tester = command_tester_factory("check", poetry=poetry_with_up_to_date_lockfile)
status_code = tester.execute(options)
expected = "All set!\n"
assert tester.io.fetch_output() == expected

# exit with an error
assert status_code == 0
10 changes: 8 additions & 2 deletions tests/console/commands/test_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def poetry_with_invalid_lockfile(
return _project_factory("invalid_lock", project_factory, fixture_dir)


def test_lock_check_outdated(
def test_lock_check_outdated_legacy(
command_tester_factory: CommandTesterFactory,
poetry_with_outdated_lockfile: Poetry,
http: type[httpretty.httpretty],
Expand All @@ -105,6 +105,7 @@ def test_lock_check_outdated(
tester = command_tester_factory("lock", poetry=poetry_with_outdated_lockfile)
status_code = tester.execute("--check")
expected = (
"poetry lock --check is deprecated, use `poetry check --lock` instead.\n"
"Error: poetry.lock is not consistent with pyproject.toml. "
"Run `poetry lock [--no-update]` to fix it.\n"
)
Expand All @@ -115,7 +116,7 @@ def test_lock_check_outdated(
assert status_code == 1


def test_lock_check_up_to_date(
def test_lock_check_up_to_date_legacy(
command_tester_factory: CommandTesterFactory,
poetry_with_up_to_date_lockfile: Poetry,
http: type[httpretty.httpretty],
Expand All @@ -133,6 +134,11 @@ def test_lock_check_up_to_date(
expected = "poetry.lock is consistent with pyproject.toml.\n"
assert tester.io.fetch_output() == expected

expected_error = (
"poetry lock --check is deprecated, use `poetry check --lock` instead.\n"
)
assert tester.io.fetch_error() == expected_error

# exit with an error
assert status_code == 0

Expand Down