Skip to content

Commit

Permalink
Moving "poetry lock --check" to "poetry check --lock" (#8015)
Browse files Browse the repository at this point in the history
  • Loading branch information
samypr100 authored Jun 14, 2023
1 parent 1fc9482 commit 6d57e84
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 9 deletions.
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`.

## 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

0 comments on commit 6d57e84

Please sign in to comment.