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

feat: Support "automated install" usage via make #7401

Closed
wants to merge 1 commit into from
Closed
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
6 changes: 2 additions & 4 deletions src/poetry/console/commands/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,10 +241,7 @@ def handle(self) -> int:
)
)

# Refresh the locker
self.poetry.set_locker(
self.poetry.locker.__class__(self.poetry.locker.lock, poetry_content)
)
self.poetry.locker.refresh(poetry_content)
self.installer.set_locker(self.poetry.locker)

# Cosmetic new line
Expand All @@ -264,6 +261,7 @@ def handle(self) -> int:
if status == 0 and not self.option("dry-run"):
assert isinstance(content, TOMLDocument)
self.poetry.file.write(content)
self.installer.touch_lockfile()

return status

Expand Down
6 changes: 2 additions & 4 deletions src/poetry/console/commands/remove.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,7 @@ def handle(self) -> int:
"The following packages were not found: " + ", ".join(sorted(not_found))
)

# Refresh the locker
self.poetry.set_locker(
self.poetry.locker.__class__(self.poetry.locker.lock, poetry_content)
)
self.poetry.locker.refresh(poetry_content)
self.installer.set_locker(self.poetry.locker)

self.installer.set_package(self.poetry.package)
Expand All @@ -122,6 +119,7 @@ def handle(self) -> int:
if not self.option("dry-run") and status == 0:
assert isinstance(content, TOMLDocument)
self.poetry.file.write(content)
self.installer.touch_lockfile()

return status

Expand Down
3 changes: 3 additions & 0 deletions src/poetry/installation/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ def lock(self, update: bool = True) -> Installer:

return self

def touch_lockfile(self) -> None:
self._locker.lock.touch()

def is_updating(self) -> bool:
return self._update

Expand Down
15 changes: 15 additions & 0 deletions src/poetry/packages/locker.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
from typing import TypeVar
from typing import cast

from packaging.utils import canonicalize_name
Expand All @@ -34,10 +35,13 @@
from poetry.core.packages.file_dependency import FileDependency
from poetry.core.packages.url_dependency import URLDependency
from poetry.core.packages.vcs_dependency import VCSDependency
from tomlkit.container import Container
from tomlkit.toml_document import TOMLDocument

from poetry.repositories.lockfile_repository import LockfileRepository

Self = TypeVar("Self", bound="Locker")

logger = logging.getLogger(__name__)
_GENERATED_IDENTIFIER = "@" + "generated"
GENERATED_COMMENT = (
Expand Down Expand Up @@ -90,6 +94,17 @@ def is_fresh(self) -> bool:

return False

def refresh(
self: Self, new_config: dict[str, Any] | Container | None = None
) -> Self:
"""
Refreshes the locker using new_config (or existing config, if not provided).
"""
self._local_config = self._local_config if new_config is None else new_config
self._lock_data = None
self._content_hash = self._get_content_hash()
return self

def locked_repository(self) -> LockfileRepository:
"""
Searches and returns a repository of locked packages.
Expand Down
59 changes: 45 additions & 14 deletions tests/console/commands/test_add.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from poetry.repositories.legacy_repository import LegacyRepository
from tests.helpers import get_dependency
from tests.helpers import get_package
from tests.helpers import modtime


if TYPE_CHECKING:
Expand Down Expand Up @@ -151,6 +152,7 @@ def test_add_no_constraint_editable_error(
assert tester.io.fetch_error() == expected
assert tester.command.installer.executor.installations_count == 0
assert content == app.poetry.file.read()["tool"]["poetry"]
assert not app.poetry.locker.is_locked()


def test_add_equal_constraint(
Expand Down Expand Up @@ -1193,6 +1195,18 @@ def test_add_with_lock(
assert content_hash != app.poetry.locker.lock_data["metadata"]["content-hash"]


def test_add_refreshes_lockfile_modtime(
app: PoetryTestApplication, repo: TestRepository, tester: CommandTester
):
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(get_package("cachy", "0.2.0"))

tester.execute("cachy")

assert app.poetry.locker.is_locked()
assert modtime(app.poetry.locker.lock) >= modtime(app.poetry.file)


def test_add_no_constraint_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
Expand Down Expand Up @@ -2145,29 +2159,45 @@ def test_add_with_lock_old_installer(
assert old_tester.io.fetch_output() == expected


def test_add_refreshes_lockfile_modtime_old_installer(
app: PoetryTestApplication,
repo: TestRepository,
installer: NoopInstaller,
old_tester: CommandTester,
):
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(get_package("cachy", "0.2.0"))

old_tester.execute("cachy")

assert app.poetry.locker.is_locked()
assert modtime(app.poetry.locker.lock) >= modtime(app.poetry.file)


def test_add_keyboard_interrupt_restore_content(
poetry_with_up_to_date_lockfile: Poetry,
repo: TestRepository,
command_tester_factory: CommandTesterFactory,
mocker: MockerFixture,
):
tester = command_tester_factory("add", poetry=poetry_with_up_to_date_lockfile)
poetry = poetry_with_up_to_date_lockfile
tester = command_tester_factory("add", poetry=poetry)

mocker.patch(
"poetry.installation.installer.Installer.run", side_effect=KeyboardInterrupt()
)
original_pyproject_content = poetry_with_up_to_date_lockfile.file.read()
original_lockfile_content = poetry_with_up_to_date_lockfile._locker.lock_data
original_pyproject_content = poetry.file.read()
original_lockfile_content = poetry._locker.lock_data
original_lockfile_modtime = modtime(poetry.locker.lock)

repo.add_package(get_package("cachy", "0.2.0"))
repo.add_package(get_package("docker", "4.3.1"))

tester.execute("cachy")

assert poetry_with_up_to_date_lockfile.file.read() == original_pyproject_content
assert (
poetry_with_up_to_date_lockfile._locker.lock_data == original_lockfile_content
)
assert poetry.file.read() == original_pyproject_content
assert poetry._locker.lock_data == original_lockfile_content
assert modtime(poetry.locker.lock) == original_lockfile_modtime


@pytest.mark.parametrize(
Expand All @@ -2183,17 +2213,18 @@ def test_add_with_dry_run_keep_files_intact(
repo: TestRepository,
command_tester_factory: CommandTesterFactory,
):
tester = command_tester_factory("add", poetry=poetry_with_up_to_date_lockfile)
poetry = poetry_with_up_to_date_lockfile
tester = command_tester_factory("add", poetry=poetry)

original_pyproject_content = poetry_with_up_to_date_lockfile.file.read()
original_lockfile_content = poetry_with_up_to_date_lockfile._locker.lock_data
original_pyproject_content = poetry.file.read()
original_lockfile_content = poetry._locker.lock_data
original_lockfile_modtime = modtime(poetry.locker.lock)

repo.add_package(get_package("cachy", "0.2.0"))
repo.add_package(get_package("docker", "4.3.1"))

tester.execute(command)

assert poetry_with_up_to_date_lockfile.file.read() == original_pyproject_content
assert (
poetry_with_up_to_date_lockfile._locker.lock_data == original_lockfile_content
)
assert poetry.file.read() == original_pyproject_content
assert poetry._locker.lock_data == original_lockfile_content
assert modtime(poetry.locker.lock) == original_lockfile_modtime
22 changes: 15 additions & 7 deletions tests/console/commands/test_remove.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from poetry.factory import Factory
from tests.helpers import get_package
from tests.helpers import modtime


if TYPE_CHECKING:
Expand Down Expand Up @@ -98,6 +99,8 @@ def test_remove_without_specific_group_removes_from_all_groups(

assert expected in string_content

assert modtime(app.poetry.locker.lock) >= modtime(app.poetry.file)


def test_remove_without_specific_group_removes_from_specific_groups(
tester: CommandTester,
Expand Down Expand Up @@ -155,6 +158,8 @@ def test_remove_without_specific_group_removes_from_specific_groups(

assert expected in string_content

assert modtime(app.poetry.locker.lock) >= modtime(app.poetry.file)


def test_remove_does_not_live_empty_groups(
tester: CommandTester,
Expand Down Expand Up @@ -275,27 +280,30 @@ def test_remove_command_should_not_write_changes_upon_installer_errors(
mocker.patch("poetry.installation.installer.Installer.run", return_value=1)

original_content = app.poetry.file.read().as_string()
original_lockfile_modtime = modtime(app.poetry.locker.lock)

tester.execute("foo")

assert app.poetry.file.read().as_string() == original_content
assert modtime(app.poetry.locker.lock) == original_lockfile_modtime


def test_remove_with_dry_run_keep_files_intact(
poetry_with_up_to_date_lockfile: Poetry,
repo: TestRepository,
command_tester_factory: CommandTesterFactory,
):
tester = command_tester_factory("remove", poetry=poetry_with_up_to_date_lockfile)
poetry = poetry_with_up_to_date_lockfile
tester = command_tester_factory("remove", poetry=poetry)

original_pyproject_content = poetry_with_up_to_date_lockfile.file.read()
original_lockfile_content = poetry_with_up_to_date_lockfile._locker.lock_data
original_pyproject_content = poetry.file.read()
original_lockfile_content = poetry._locker.lock_data
original_lockfile_modtime = modtime(poetry.locker.lock)

repo.add_package(get_package("docker", "4.3.1"))

tester.execute("docker --dry-run")

assert poetry_with_up_to_date_lockfile.file.read() == original_pyproject_content
assert (
poetry_with_up_to_date_lockfile._locker.lock_data == original_lockfile_content
)
assert poetry.file.read() == original_pyproject_content
assert poetry._locker.lock_data == original_lockfile_content
assert modtime(poetry.locker.lock) == original_lockfile_modtime
4 changes: 4 additions & 0 deletions tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ def copy_or_symlink(source: Path, dest: Path) -> None:
os.symlink(str(source), str(dest))


def modtime(path: str | Path | TOMLFile) -> float:
return os.path.getmtime(str(path))


class MockDulwichRepo:
def __init__(self, root: Path | str, **__: Any) -> None:
self.path = str(root)
Expand Down