diff --git a/docs/cli.md b/docs/cli.md
index b5913b69040..5a5fd878f7e 100644
--- a/docs/cli.md
+++ b/docs/cli.md
@@ -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.
@@ -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.
@@ -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
@@ -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
diff --git a/src/poetry/console/commands/check.py b/src/poetry/console/commands/check.py
index 3ca831e7213..733f0a7c5d5 100644
--- a/src/poetry/console/commands/check.py
+++ b/src/poetry/console/commands/check.py
@@ -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 pyproject.toml file."
+ description = (
+ "Validates the content of the pyproject.toml> file and its"
+ " consistency with the poetry.lock file."
+ )
+
+ options = [
+ option(
+ "lock",
+ None,
+ (
+ "Checks that poetry.lock> exists for the current"
+ " version of pyproject.toml>."
+ ),
+ ),
+ ]
def validate_classifiers(
self, project_classifiers: set[str]
@@ -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!")
diff --git a/src/poetry/console/commands/lock.py b/src/poetry/console/commands/lock.py
index c87f1359dc7..87273a66776 100644
--- a/src/poetry/console/commands/lock.py
+++ b/src/poetry/console/commands/lock.py
@@ -18,7 +18,8 @@ class LockCommand(InstallerCommand):
None,
(
"Check that the poetry.lock> file corresponds to the current"
- " version of pyproject.toml>."
+ " version of pyproject.toml>. (Deprecated>) Use"
+ " poetry check --lock> instead."
),
),
]
@@ -36,6 +37,10 @@ class LockCommand(InstallerCommand):
def handle(self) -> int:
if self.option("check"):
+ self.line_error(
+ "poetry lock --check is deprecated, use `poetry"
+ " check --lock` instead."
+ )
if self.poetry.locker.is_locked() and self.poetry.locker.is_fresh():
self.line("poetry.lock is consistent with pyproject.toml.")
return 0
diff --git a/tests/console/commands/test_check.py b/tests/console/commands/test_check.py
index 323364c8def..6347d4585fb 100644
--- a/tests/console/commands/test_check.py
+++ b/tests/console/commands/test_check.py
@@ -4,13 +4,19 @@
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()
@@ -18,6 +24,36 @@ 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()
@@ -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,\
@@ -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
diff --git a/tests/console/commands/test_lock.py b/tests/console/commands/test_lock.py
index 7a910caccf7..26e0b98277c 100644
--- a/tests/console/commands/test_lock.py
+++ b/tests/console/commands/test_lock.py
@@ -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],
@@ -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"
)
@@ -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],
@@ -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