Commit cff1baa1 by Arun Babu Neelicattu

deprecate plugin commands and route to self

parent c88f5b52
...@@ -278,6 +278,14 @@ The add command adds required packages to your <comment>pyproject.toml</> and in ...@@ -278,6 +278,14 @@ The add command adds required packages to your <comment>pyproject.toml</> and in
return existing_packages return existing_packages
@property
def _hint_update_packages(self) -> str:
return (
"\nIf you want to update it to the latest compatible version, you can use"
" `poetry update package`.\nIf you prefer to upgrade it to the latest"
" available version, you can use `poetry add package@latest`.\n"
)
def notify_about_existing_packages(self, existing_packages: list[str]) -> None: def notify_about_existing_packages(self, existing_packages: list[str]) -> None:
self.line( self.line(
"The following packages are already present in the pyproject.toml and will" "The following packages are already present in the pyproject.toml and will"
...@@ -285,8 +293,4 @@ The add command adds required packages to your <comment>pyproject.toml</> and in ...@@ -285,8 +293,4 @@ The add command adds required packages to your <comment>pyproject.toml</> and in
) )
for name in existing_packages: for name in existing_packages:
self.line(f" • <c1>{name}</c1>") self.line(f" • <c1>{name}</c1>")
self.line( self.line(self._hint_update_packages)
"\nIf you want to update it to the latest compatible version, you can use"
" `poetry update package`.\nIf you prefer to upgrade it to the latest"
" available version, you can use `poetry add package@latest`.\n"
)
from __future__ import annotations from __future__ import annotations
import os
from typing import Any
from typing import cast from typing import cast
from cleo.helpers import argument from cleo.helpers import argument
from cleo.helpers import option from cleo.helpers import option
from cleo.io.inputs.string_input import StringInput
from cleo.io.io import IO
from poetry.console.application import Application from poetry.console.application import Application
from poetry.console.commands.init import InitCommand from poetry.console.commands.init import InitCommand
from poetry.console.commands.update import UpdateCommand from poetry.console.commands.self.add import SelfAddCommand
class PluginAddCommand(InitCommand): class PluginAddCommand(InitCommand):
...@@ -31,146 +30,33 @@ class PluginAddCommand(InitCommand): ...@@ -31,146 +30,33 @@ class PluginAddCommand(InitCommand):
" --verbose).", " --verbose).",
) )
] ]
deprecation = (
help = """ "<warning>This command is deprecated. Use <c2>self add</> command instead."
"</warning>"
)
help = f"""
The <c1>plugin add</c1> command installs Poetry plugins globally. The <c1>plugin add</c1> command installs Poetry plugins globally.
It works similarly to the <c1>add</c1> command: It works similarly to the <c1>add</c1> command:
If you do not specify a version constraint, poetry will choose a suitable one based on\ {SelfAddCommand.examples}
the available package versions.
{deprecation}
You can specify a package in the following forms:
- A single name (<b>requests</b>)
- A name and a constraint (<b>requests@^2.23.0</b>)
- A git url (<b>git+https://github.com/python-poetry/poetry.git</b>)
- A git url with a revision\
(<b>git+https://github.com/python-poetry/poetry.git#develop</b>)
- A git SSH url (<b>git+ssh://github.com/python-poetry/poetry.git</b>)
- A git SSH url with a revision\
(<b>git+ssh://github.com/python-poetry/poetry.git#develop</b>)
- A file path (<b>../my-package/my-package.whl</b>)
- A directory (<b>../my-package/</b>)
- A url (<b>https://example.com/packages/my-package-0.1.0.tar.gz</b>)\
""" """
def handle(self) -> int: def handle(self) -> int:
from pathlib import Path self.line_error(self.deprecation)
import tomlkit
from cleo.io.inputs.string_input import StringInput
from cleo.io.io import IO
from poetry.core.packages.project_package import ProjectPackage
from poetry.core.pyproject.toml import PyProjectTOML
from poetry.core.semver.helpers import parse_constraint
from poetry.factory import Factory
from poetry.repositories.installed_repository import InstalledRepository
from poetry.utils.env import EnvManager
plugins = self.argument("plugins")
# Plugins should be installed in the system env to be globally available
system_env = EnvManager.get_system_env(naive=True)
env_dir = Path(os.getenv("POETRY_HOME") or system_env.path)
# We check for the plugins existence first.
if env_dir.joinpath("pyproject.toml").exists():
pyproject: dict[str, Any] = tomlkit.loads(
env_dir.joinpath("pyproject.toml").read_text(encoding="utf-8")
)
poetry_content = pyproject["tool"]["poetry"]
existing_packages = self.get_existing_packages_from_input(
plugins, poetry_content, "dependencies"
)
if existing_packages:
self.notify_about_existing_packages(existing_packages)
plugins = [plugin for plugin in plugins if plugin not in existing_packages]
if not plugins:
return 0
plugins = self._determine_requirements(plugins)
# We retrieve the packages installed in the system environment.
# We assume that this environment will be a self contained virtual environment
# built by the official installer or by pipx.
# If not, it might lead to side effects since other installed packages might not
# be required by Poetry but still be taken into account when resolving
# dependencies.
installed_repository = InstalledRepository.load(
system_env, with_dependencies=True
)
root_package = None application = cast(Application, self.application)
for package in installed_repository.packages: command: SelfAddCommand = cast(SelfAddCommand, application.find("self add"))
if package.name == "poetry": application._configure_installer(command, self.io)
root_package = ProjectPackage(package.name, package.version)
for dependency in package.requires:
root_package.add_dependency(dependency)
break
assert root_package is not None
root_package.python_versions = ".".join(
str(v) for v in system_env.version_info[:3]
)
# We create a `pyproject.toml` file based on all the information
# we have about the current environment.
if not env_dir.joinpath("pyproject.toml").exists():
Factory.create_pyproject_from_package(
root_package,
env_dir,
)
# We add the plugins to the dependencies section of the previously
# created `pyproject.toml` file
pyproject_toml = PyProjectTOML(env_dir.joinpath("pyproject.toml"))
poetry_content = pyproject_toml.poetry_config
poetry_dependency_section = poetry_content["dependencies"]
plugin_names = []
for plugin in plugins:
if "version" in plugin:
# Validate version constraint
parse_constraint(plugin["version"])
constraint: dict[str, Any] = tomlkit.inline_table()
for name, value in plugin.items():
if name == "name":
continue
constraint[name] = value
if len(constraint) == 1 and "version" in constraint:
constraint = constraint["version"]
poetry_dependency_section[plugin["name"]] = constraint
plugin_names.append(plugin["name"])
pyproject_toml.save() argv: list[str] = ["add", *self.argument("plugins")]
# From this point forward, all the logic will be deferred to if self.option("--dry-run"):
# the update command, by using the previously created `pyproject.toml`
# file.
application = cast(Application, self.application)
update_command: UpdateCommand = cast(UpdateCommand, application.find("update"))
# We won't go through the event dispatching done by the application
# so we need to configure the command manually
update_command.set_poetry(Factory().create_poetry(env_dir))
update_command.set_env(system_env)
application._configure_installer(update_command, self._io)
argv = ["update"] + plugin_names
if self.option("dry-run"):
argv.append("--dry-run") argv.append("--dry-run")
exit_code: int = update_command.run( exit_code: int = command.run(
IO( IO(
StringInput(" ".join(argv)), StringInput(" ".join(argv)),
self._io.output, self._io.output,
...@@ -178,30 +64,3 @@ You can specify a package in the following forms: ...@@ -178,30 +64,3 @@ You can specify a package in the following forms:
) )
) )
return exit_code return exit_code
def get_existing_packages_from_input(
self, packages: list[str], poetry_content: dict[str, Any], target_section: str
) -> list[str]:
existing_packages = []
for name in packages:
for key in poetry_content[target_section]:
if key.lower() == name.lower():
existing_packages.append(name)
return existing_packages
def notify_about_existing_packages(self, existing_packages: list[str]) -> None:
self.line(
"The following plugins are already present in the "
"<c2>pyproject.toml</c2> file and will be skipped:\n"
)
for name in existing_packages:
self.line(f" • <c1>{name}</c1>")
self.line(
"\nIf you want to update it to the latest compatible version, "
"you can use `<c2>poetry plugin update package</c2>`.\n"
"If you prefer to upgrade it to the latest available version, "
"you can use `<c2>poetry plugin add package@latest</c2>`.\n"
)
from __future__ import annotations from __future__ import annotations
import os
from typing import cast from typing import cast
from cleo.helpers import argument from cleo.helpers import argument
from cleo.helpers import option from cleo.helpers import option
from cleo.io.inputs.string_input import StringInput
from cleo.io.io import IO
from poetry.console.application import Application from poetry.console.application import Application
from poetry.console.commands.command import Command from poetry.console.commands.command import Command
from poetry.console.commands.remove import RemoveCommand from poetry.console.commands.self.remove import SelfRemoveCommand
class PluginRemoveCommand(Command): class PluginRemoveCommand(Command):
...@@ -31,35 +31,26 @@ class PluginRemoveCommand(Command): ...@@ -31,35 +31,26 @@ class PluginRemoveCommand(Command):
) )
] ]
def handle(self) -> int: help = (
from pathlib import Path "<warning>This command is deprecated. Use <c2>self remove</> command instead."
"</warning>"
from cleo.io.inputs.string_input import StringInput )
from cleo.io.io import IO
from poetry.factory import Factory
from poetry.utils.env import EnvManager
plugins = self.argument("plugins") def handle(self) -> int:
self.line_error(self.help)
system_env = EnvManager.get_system_env(naive=True)
env_dir = Path(os.getenv("POETRY_HOME") or system_env.path)
# From this point forward, all the logic will be deferred to
# the remove command, by using the global `pyproject.toml` file.
application = cast(Application, self.application) application = cast(Application, self.application)
remove_command: RemoveCommand = cast(RemoveCommand, application.find("remove")) command: SelfRemoveCommand = cast(
# We won't go through the event dispatching done by the application SelfRemoveCommand, application.find("self remove")
# so we need to configure the command manually )
remove_command.set_poetry(Factory().create_poetry(env_dir)) application._configure_installer(command, self.io)
remove_command.set_env(system_env)
application._configure_installer(remove_command, self._io) argv: list[str] = ["remove", *self.argument("plugins")]
argv = ["remove"] + plugins if self.option("--dry-run"):
if self.option("dry-run"):
argv.append("--dry-run") argv.append("--dry-run")
exit_code: int = remove_command.run( exit_code: int = command.run(
IO( IO(
StringInput(" ".join(argv)), StringInput(" ".join(argv)),
self._io.output, self._io.output,
......
from __future__ import annotations from __future__ import annotations
from poetry.core.semver.version import Version
from poetry.__version__ import __version__
from poetry.console.commands.add import AddCommand from poetry.console.commands.add import AddCommand
from poetry.console.commands.self.self_command import SelfCommand from poetry.console.commands.self.self_command import SelfCommand
...@@ -21,3 +24,17 @@ file. ...@@ -21,3 +24,17 @@ file.
{AddCommand.examples} {AddCommand.examples}
""" """
@property
def _hint_update_packages(self) -> str:
version = Version.parse(__version__)
flags = ""
if not version.is_stable():
flags = " --preview"
return (
"\nIf you want to update it to the latest compatible version, you can use"
f" `poetry self update{flags}`.\nIf you prefer to upgrade it to the latest"
" available version, you can use `poetry self add package@latest`.\n"
)
from __future__ import annotations
from typing import TYPE_CHECKING
import pytest
from poetry.core.packages.package import Package
from poetry.__version__ import __version__
from poetry.factory import Factory
from poetry.repositories.installed_repository import InstalledRepository
from poetry.repositories.pool import Pool
from poetry.utils.env import EnvManager
if TYPE_CHECKING:
from cleo.io.io import IO
from pytest_mock import MockerFixture
from poetry.config.config import Config
from poetry.config.source import Source
from poetry.poetry import Poetry
from poetry.repositories import Repository
from poetry.utils.env import MockEnv
from tests.helpers import TestRepository
from tests.types import SourcesFactory
@pytest.fixture()
def installed() -> InstalledRepository:
repository = InstalledRepository()
repository.add_package(Package("poetry", __version__))
return repository
def configure_sources_factory(repo: TestRepository) -> SourcesFactory:
def _configure_sources(
poetry: Poetry,
sources: Source,
config: Config,
io: IO,
disable_cache: bool = False,
) -> None:
pool = Pool()
pool.add_repository(repo)
poetry.set_pool(pool)
return _configure_sources
@pytest.fixture(autouse=True)
def setup_mocks(
mocker: MockerFixture,
env: MockEnv,
repo: TestRepository,
installed: Repository,
) -> None:
mocker.patch.object(EnvManager, "get_system_env", return_value=env)
mocker.patch.object(InstalledRepository, "load", return_value=installed)
mocker.patch.object(
Factory, "configure_sources", side_effect=configure_sources_factory(repo)
)
from __future__ import annotations
from typing import TYPE_CHECKING
import pytest
from poetry.core.packages.package import Package
from poetry.__version__ import __version__
from poetry.repositories import Pool
from poetry.utils.env import EnvManager
if TYPE_CHECKING:
import httpretty
from pytest_mock import MockerFixture
from poetry.repositories.repository import Repository
from poetry.utils.env import VirtualEnv
from tests.helpers import TestRepository
@pytest.fixture(autouse=True)
def _patch_repos(repo: TestRepository, installed: Repository) -> None:
poetry = Package("poetry", __version__)
repo.add_package(poetry)
installed.add_package(poetry)
@pytest.fixture(autouse=True)
def save_environ(environ: None) -> Repository:
yield
@pytest.fixture()
def pool(repo: TestRepository) -> Pool:
return Pool([repo])
@pytest.fixture(autouse=True)
def setup_mocks(
mocker: MockerFixture,
tmp_venv: VirtualEnv,
installed: Repository,
pool: Pool,
http: type[httpretty.httpretty],
) -> None:
mocker.patch.object(EnvManager, "get_system_env", return_value=tmp_venv)
mocker.patch("poetry.repositories.pool.Pool.find_packages", pool.find_packages)
mocker.patch("poetry.repositories.pool.Pool.package", pool.package)
mocker.patch("poetry.installation.executor.pip_install")
mocker.patch(
"poetry.installation.installer.Installer._get_installed",
return_value=installed,
)
from __future__ import annotations
from typing import TYPE_CHECKING
import pytest
from poetry.core.packages.package import Package
from poetry.__version__ import __version__
if TYPE_CHECKING:
from cleo.testers.command_tester import CommandTester
from tests.helpers import TestRepository
from tests.types import CommandTesterFactory
@pytest.fixture()
def tester(command_tester_factory: CommandTesterFactory) -> CommandTester:
return command_tester_factory("plugin add")
def test_deprecation_warning(tester: CommandTester, repo: TestRepository) -> None:
repo.add_package(Package("poetry", __version__))
repo.add_package(Package("poetry-plugin", "1.0"))
tester.execute("poetry-plugin")
assert (
tester.io.fetch_error()
== "This command is deprecated. Use self add command instead.\n"
)
from __future__ import annotations
from typing import TYPE_CHECKING
import pytest
from poetry.core.packages.dependency import Dependency
from poetry.core.packages.package import Package
from poetry.core.packages.project_package import ProjectPackage
from poetry.__version__ import __version__
from poetry.console.commands.self.self_command import SelfCommand
from poetry.factory import Factory
from tests.console.commands.self.utils import get_self_command_dependencies
if TYPE_CHECKING:
from cleo.testers.command_tester import CommandTester
from tests.helpers import TestRepository
from tests.types import CommandTesterFactory
@pytest.fixture()
def tester(command_tester_factory: CommandTesterFactory) -> CommandTester:
return command_tester_factory("plugin remove")
def test_deprecation_warning(tester: CommandTester, repo: TestRepository) -> None:
plugin = Package("poetry-plugin", "1.2.3")
repo.add_package(Package("poetry", __version__))
repo.add_package(plugin)
package = ProjectPackage("poetry-instance", __version__)
package.add_dependency(
Dependency(plugin.name, "^1.2.3", groups=[SelfCommand.ADDITIONAL_PACKAGE_GROUP])
)
content = Factory.create_pyproject_from_package(package)
system_pyproject_file = SelfCommand.get_default_system_pyproject_file()
system_pyproject_file.write_text(content.as_string(), encoding="utf-8")
dependencies = get_self_command_dependencies(locked=False)
assert "poetry-plugin" in dependencies
tester.execute("poetry-plugin")
assert (
tester.io.fetch_error()
== "This command is deprecated. Use self remove command instead.\n"
)
dependencies = get_self_command_dependencies()
assert "poetry-plugin" not in dependencies
assert not dependencies
...@@ -6,51 +6,38 @@ import pytest ...@@ -6,51 +6,38 @@ import pytest
from poetry.core.packages.package import Package from poetry.core.packages.package import Package
from poetry.console.commands.self.self_command import SelfCommand
from poetry.factory import Factory from poetry.factory import Factory
from tests.console.commands.self.utils import get_self_command_dependencies
if TYPE_CHECKING: if TYPE_CHECKING:
from cleo.testers.command_tester import CommandTester from cleo.testers.command_tester import CommandTester
from pytest_mock import MockerFixture
from poetry.console.commands.update import UpdateCommand
from poetry.repositories import Repository
from poetry.utils.env import MockEnv
from tests.helpers import PoetryTestApplication
from tests.helpers import TestRepository from tests.helpers import TestRepository
from tests.types import CommandTesterFactory from tests.types import CommandTesterFactory
@pytest.fixture() @pytest.fixture()
def tester(command_tester_factory: CommandTesterFactory) -> CommandTester: def tester(command_tester_factory: CommandTesterFactory) -> CommandTester:
return command_tester_factory("plugin add") return command_tester_factory("self add")
def assert_plugin_add_result( def assert_plugin_add_result(
tester: CommandTester, tester: CommandTester,
app: PoetryTestApplication,
env: MockEnv,
expected: str, expected: str,
constraint: str | dict[str, str], constraint: str | dict[str, str],
) -> None: ) -> None:
assert tester.io.fetch_output() == expected assert tester.io.fetch_output() == expected
dependencies = get_self_command_dependencies()
update_command: UpdateCommand = app.find("update") assert "poetry-plugin" in dependencies
assert update_command.poetry.file.parent == env.path assert dependencies["poetry-plugin"] == constraint
assert update_command.poetry.locker.lock.parent == env.path
assert update_command.poetry.locker.lock.exists()
content = update_command.poetry.file.read()["tool"]["poetry"]
assert "poetry-plugin" in content["dependencies"]
assert content["dependencies"]["poetry-plugin"] == constraint
def test_add_no_constraint( def test_add_no_constraint(
app: PoetryTestApplication,
repo: TestRepository,
tester: CommandTester, tester: CommandTester,
env: MockEnv, repo: TestRepository,
installed: Repository,
): ):
repo.add_package(Package("poetry-plugin", "0.1.0")) repo.add_package(Package("poetry-plugin", "0.1.0"))
...@@ -58,6 +45,7 @@ def test_add_no_constraint( ...@@ -58,6 +45,7 @@ def test_add_no_constraint(
expected = """\ expected = """\
Using version ^0.1.0 for poetry-plugin Using version ^0.1.0 for poetry-plugin
Updating dependencies Updating dependencies
Resolving dependencies... Resolving dependencies...
...@@ -67,22 +55,19 @@ Package operations: 1 install, 0 updates, 0 removals ...@@ -67,22 +55,19 @@ Package operations: 1 install, 0 updates, 0 removals
• Installing poetry-plugin (0.1.0) • Installing poetry-plugin (0.1.0)
""" """
assert_plugin_add_result(tester, app, env, expected, "^0.1.0") assert_plugin_add_result(tester, expected, "^0.1.0")
def test_add_with_constraint( def test_add_with_constraint(
app: PoetryTestApplication,
repo: TestRepository,
tester: CommandTester, tester: CommandTester,
env: MockEnv, repo: TestRepository,
installed: Repository,
): ):
repo.add_package(Package("poetry-plugin", "0.1.0")) repo.add_package(Package("poetry-plugin", "0.1.0"))
repo.add_package(Package("poetry-plugin", "0.2.0")) repo.add_package(Package("poetry-plugin", "0.2.0"))
tester.execute("poetry-plugin@^0.2.0") tester.execute("poetry-plugin@^0.2.0")
expected = """\ expected = """
Updating dependencies Updating dependencies
Resolving dependencies... Resolving dependencies...
...@@ -93,21 +78,18 @@ Package operations: 1 install, 0 updates, 0 removals ...@@ -93,21 +78,18 @@ Package operations: 1 install, 0 updates, 0 removals
• Installing poetry-plugin (0.2.0) • Installing poetry-plugin (0.2.0)
""" """
assert_plugin_add_result(tester, app, env, expected, "^0.2.0") assert_plugin_add_result(tester, expected, "^0.2.0")
def test_add_with_git_constraint( def test_add_with_git_constraint(
app: PoetryTestApplication,
repo: TestRepository,
tester: CommandTester, tester: CommandTester,
env: MockEnv, repo: TestRepository,
installed: Repository,
): ):
repo.add_package(Package("pendulum", "2.0.5")) repo.add_package(Package("pendulum", "2.0.5"))
tester.execute("git+https://github.com/demo/poetry-plugin.git") tester.execute("git+https://github.com/demo/poetry-plugin.git")
expected = """\ expected = """
Updating dependencies Updating dependencies
Resolving dependencies... Resolving dependencies...
...@@ -120,23 +102,20 @@ Package operations: 2 installs, 0 updates, 0 removals ...@@ -120,23 +102,20 @@ Package operations: 2 installs, 0 updates, 0 removals
""" """
assert_plugin_add_result( assert_plugin_add_result(
tester, app, env, expected, {"git": "https://github.com/demo/poetry-plugin.git"} tester, expected, {"git": "https://github.com/demo/poetry-plugin.git"}
) )
def test_add_with_git_constraint_with_extras( def test_add_with_git_constraint_with_extras(
app: PoetryTestApplication,
repo: TestRepository,
tester: CommandTester, tester: CommandTester,
env: MockEnv, repo: TestRepository,
installed: Repository,
): ):
repo.add_package(Package("pendulum", "2.0.5")) repo.add_package(Package("pendulum", "2.0.5"))
repo.add_package(Package("tomlkit", "0.7.0")) repo.add_package(Package("tomlkit", "0.7.0"))
tester.execute("git+https://github.com/demo/poetry-plugin.git[foo]") tester.execute("git+https://github.com/demo/poetry-plugin.git[foo]")
expected = """\ expected = """
Updating dependencies Updating dependencies
Resolving dependencies... Resolving dependencies...
...@@ -151,8 +130,6 @@ Package operations: 3 installs, 0 updates, 0 removals ...@@ -151,8 +130,6 @@ Package operations: 3 installs, 0 updates, 0 removals
assert_plugin_add_result( assert_plugin_add_result(
tester, tester,
app,
env,
expected, expected,
{ {
"git": "https://github.com/demo/poetry-plugin.git", "git": "https://github.com/demo/poetry-plugin.git",
...@@ -162,24 +139,22 @@ Package operations: 3 installs, 0 updates, 0 removals ...@@ -162,24 +139,22 @@ Package operations: 3 installs, 0 updates, 0 removals
def test_add_existing_plugin_warns_about_no_operation( def test_add_existing_plugin_warns_about_no_operation(
app: PoetryTestApplication,
repo: TestRepository,
tester: CommandTester, tester: CommandTester,
env: MockEnv, repo: TestRepository,
installed: Repository, installed: TestRepository,
): ):
env.path.joinpath("pyproject.toml").write_text( SelfCommand.get_default_system_pyproject_file().write_text(
"""\ f"""\
[tool.poetry] [tool.poetry]
name = "poetry" name = "poetry-instance"
version = "1.2.0" version = "1.2.0"
description = "Python dependency management and packaging made easy." description = "Python dependency management and packaging made easy."
authors = [ authors = []
"Sébastien Eustace <sebastien@eustace.io>"
]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.6" python = "^3.6"
[tool.poetry.group.{SelfCommand.ADDITIONAL_PACKAGE_GROUP}.dependencies]
poetry-plugin = "^1.2.3" poetry-plugin = "^1.2.3"
""", """,
encoding="utf-8", encoding="utf-8",
...@@ -191,46 +166,35 @@ poetry-plugin = "^1.2.3" ...@@ -191,46 +166,35 @@ poetry-plugin = "^1.2.3"
tester.execute("poetry-plugin") tester.execute("poetry-plugin")
expected = """\ expected = f"""\
The following plugins are already present in the pyproject.toml file and will be\ The following packages are already present in the pyproject.toml and will be\
skipped: skipped:
• poetry-plugin • poetry-plugin
{tester.command._hint_update_packages}
If you want to update it to the latest compatible version,\ Nothing to add.
you can use `poetry plugin update package`.
If you prefer to upgrade it to the latest available version,\
you can use `poetry plugin add package@latest`.
""" """
assert tester.io.fetch_output() == expected assert tester.io.fetch_output() == expected
update_command: UpdateCommand = app.find("update")
# The update command should not have been called
assert update_command.poetry.file.parent != env.path
def test_add_existing_plugin_updates_if_requested( def test_add_existing_plugin_updates_if_requested(
app: PoetryTestApplication,
repo: TestRepository,
tester: CommandTester, tester: CommandTester,
env: MockEnv, repo: TestRepository,
installed: Repository, installed: TestRepository,
mocker: MockerFixture,
): ):
env.path.joinpath("pyproject.toml").write_text( SelfCommand.get_default_system_pyproject_file().write_text(
"""\ f"""\
[tool.poetry] [tool.poetry]
name = "poetry" name = "poetry-instance"
version = "1.2.0" version = "1.2.0"
description = "Python dependency management and packaging made easy." description = "Python dependency management and packaging made easy."
authors = [ authors = []
"Sébastien Eustace <sebastien@eustace.io>"
]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.6" python = "^3.6"
[tool.poetry.group.{SelfCommand.ADDITIONAL_PACKAGE_GROUP}.dependencies]
poetry-plugin = "^1.2.3" poetry-plugin = "^1.2.3"
""", """,
encoding="utf-8", encoding="utf-8",
...@@ -245,6 +209,7 @@ poetry-plugin = "^1.2.3" ...@@ -245,6 +209,7 @@ poetry-plugin = "^1.2.3"
expected = """\ expected = """\
Using version ^2.3.4 for poetry-plugin Using version ^2.3.4 for poetry-plugin
Updating dependencies Updating dependencies
Resolving dependencies... Resolving dependencies...
...@@ -255,15 +220,13 @@ Package operations: 0 installs, 1 update, 0 removals ...@@ -255,15 +220,13 @@ Package operations: 0 installs, 1 update, 0 removals
• Updating poetry-plugin (1.2.3 -> 2.3.4) • Updating poetry-plugin (1.2.3 -> 2.3.4)
""" """
assert_plugin_add_result(tester, app, env, expected, "^2.3.4") assert_plugin_add_result(tester, expected, "^2.3.4")
def test_adding_a_plugin_can_update_poetry_dependencies_if_needed( def test_adding_a_plugin_can_update_poetry_dependencies_if_needed(
app: PoetryTestApplication,
repo: TestRepository,
tester: CommandTester, tester: CommandTester,
env: MockEnv, repo: TestRepository,
installed: Repository, installed: TestRepository,
): ):
poetry_package = Package("poetry", "1.2.0") poetry_package = Package("poetry", "1.2.0")
poetry_package.add_dependency(Factory.create_dependency("tomlkit", "^0.7.0")) poetry_package.add_dependency(Factory.create_dependency("tomlkit", "^0.7.0"))
...@@ -282,6 +245,7 @@ def test_adding_a_plugin_can_update_poetry_dependencies_if_needed( ...@@ -282,6 +245,7 @@ def test_adding_a_plugin_can_update_poetry_dependencies_if_needed(
expected = """\ expected = """\
Using version ^1.2.3 for poetry-plugin Using version ^1.2.3 for poetry-plugin
Updating dependencies Updating dependencies
Resolving dependencies... Resolving dependencies...
...@@ -293,4 +257,4 @@ Package operations: 1 install, 1 update, 0 removals ...@@ -293,4 +257,4 @@ Package operations: 1 install, 1 update, 0 removals
• Installing poetry-plugin (1.2.3) • Installing poetry-plugin (1.2.3)
""" """
assert_plugin_add_result(tester, app, env, expected, "^1.2.3") assert_plugin_add_result(tester, expected, "^1.2.3")
...@@ -5,47 +5,40 @@ from typing import TYPE_CHECKING ...@@ -5,47 +5,40 @@ from typing import TYPE_CHECKING
import pytest import pytest
import tomlkit import tomlkit
from poetry.core.packages.dependency import Dependency
from poetry.core.packages.package import Package from poetry.core.packages.package import Package
from poetry.core.packages.project_package import ProjectPackage
from poetry.__version__ import __version__ from poetry.__version__ import __version__
from poetry.layouts.layout import POETRY_DEFAULT from poetry.console.commands.self.self_command import SelfCommand
from poetry.factory import Factory
from tests.console.commands.self.utils import get_self_command_dependencies
if TYPE_CHECKING: if TYPE_CHECKING:
from cleo.testers.command_tester import CommandTester from cleo.testers.command_tester import CommandTester
from poetry.console.commands.remove import RemoveCommand
from poetry.repositories import Repository from poetry.repositories import Repository
from poetry.utils.env import MockEnv
from tests.helpers import PoetryTestApplication
from tests.types import CommandTesterFactory from tests.types import CommandTesterFactory
@pytest.fixture() @pytest.fixture()
def tester(command_tester_factory: CommandTesterFactory) -> CommandTester: def tester(command_tester_factory: CommandTesterFactory) -> CommandTester:
return command_tester_factory("plugin remove") return command_tester_factory("self remove")
@pytest.fixture() @pytest.fixture(autouse=True)
def pyproject(env: MockEnv) -> None: def install_plugin(installed: Repository) -> None:
pyproject = tomlkit.loads(POETRY_DEFAULT) package = ProjectPackage("poetry-instance", __version__)
content = pyproject["tool"]["poetry"] plugin = Package("poetry-plugin", "1.2.3")
content["name"] = "poetry"
content["version"] = __version__
content["description"] = ""
content["authors"] = ["Sébastien Eustace <sebastien@eustace.io>"]
dependency_section = content["dependencies"]
dependency_section["python"] = "^3.6"
env.path.joinpath("pyproject.toml").write_text( package.add_dependency(
tomlkit.dumps(pyproject), encoding="utf-8" Dependency(plugin.name, "^1.2.3", groups=[SelfCommand.ADDITIONAL_PACKAGE_GROUP])
) )
content = Factory.create_pyproject_from_package(package)
system_pyproject_file = SelfCommand.get_default_system_pyproject_file()
system_pyproject_file.write_text(content.as_string(), encoding="utf-8")
@pytest.fixture(autouse=True)
def install_plugin(env: MockEnv, installed: Repository, pyproject: None) -> None:
lock_content = { lock_content = {
"package": [ "package": [
{ {
...@@ -65,29 +58,14 @@ def install_plugin(env: MockEnv, installed: Repository, pyproject: None) -> None ...@@ -65,29 +58,14 @@ def install_plugin(env: MockEnv, installed: Repository, pyproject: None) -> None
"hashes": {"poetry-plugin": []}, "hashes": {"poetry-plugin": []},
}, },
} }
system_pyproject_file.parent.joinpath("poetry.lock").write_text(
env.path.joinpath("poetry.lock").write_text(
tomlkit.dumps(lock_content), encoding="utf-8" tomlkit.dumps(lock_content), encoding="utf-8"
) )
pyproject_toml = tomlkit.loads( installed.add_package(plugin)
env.path.joinpath("pyproject.toml").read_text(encoding="utf-8")
)
content = pyproject_toml["tool"]["poetry"]
dependency_section = content["dependencies"]
dependency_section["poetry-plugin"] = "^1.2.3"
env.path.joinpath("pyproject.toml").write_text( def test_remove_installed_package(tester: CommandTester):
tomlkit.dumps(pyproject_toml), encoding="utf-8"
)
installed.add_package(Package("poetry-plugin", "1.2.3"))
def test_remove_installed_package(
app: PoetryTestApplication, tester: CommandTester, env: MockEnv
):
tester.execute("poetry-plugin") tester.execute("poetry-plugin")
expected = """\ expected = """\
...@@ -100,41 +78,31 @@ Package operations: 0 installs, 0 updates, 1 removal ...@@ -100,41 +78,31 @@ Package operations: 0 installs, 0 updates, 1 removal
• Removing poetry-plugin (1.2.3) • Removing poetry-plugin (1.2.3)
""" """
assert tester.io.fetch_output() == expected assert tester.io.fetch_output() == expected
remove_command: RemoveCommand = app.find("remove") dependencies = get_self_command_dependencies()
assert remove_command.poetry.file.parent == env.path
assert remove_command.poetry.locker.lock.parent == env.path
assert remove_command.poetry.locker.lock.exists()
assert not remove_command.installer.executor._dry_run
content = remove_command.poetry.file.read()["tool"]["poetry"] assert "poetry-plugin" not in dependencies
assert "poetry-plugin" not in content["dependencies"] assert not dependencies
def test_remove_installed_package_dry_run( def test_remove_installed_package_dry_run(tester: CommandTester):
app: PoetryTestApplication, tester: CommandTester, env: MockEnv
):
tester.execute("poetry-plugin --dry-run") tester.execute("poetry-plugin --dry-run")
expected = """\ expected = f"""\
Updating dependencies Updating dependencies
Resolving dependencies... Resolving dependencies...
Package operations: 0 installs, 0 updates, 1 removal Package operations: 0 installs, 0 updates, 1 removal, 1 skipped
• Removing poetry-plugin (1.2.3) • Removing poetry-plugin (1.2.3)
• Removing poetry-plugin (1.2.3) • Removing poetry-plugin (1.2.3)
• Installing poetry ({__version__}): Skipped for the following reason: Already \
installed
""" """
assert tester.io.fetch_output() == expected assert tester.io.fetch_output() == expected
remove_command: RemoveCommand = app.find("remove") dependencies = get_self_command_dependencies()
assert remove_command.poetry.file.parent == env.path
assert remove_command.poetry.locker.lock.parent == env.path
assert remove_command.poetry.locker.lock.exists()
assert remove_command.installer.executor._dry_run
content = remove_command.poetry.file.read()["tool"]["poetry"] assert "poetry-plugin" in dependencies
assert "poetry-plugin" in content["dependencies"]
...@@ -10,48 +10,17 @@ from poetry.core.semver.version import Version ...@@ -10,48 +10,17 @@ from poetry.core.semver.version import Version
from poetry.__version__ import __version__ from poetry.__version__ import __version__
from poetry.factory import Factory from poetry.factory import Factory
from poetry.repositories.repository import Repository
from poetry.utils.env import EnvManager
if TYPE_CHECKING: if TYPE_CHECKING:
import httpretty
from cleo.testers.command_tester import CommandTester from cleo.testers.command_tester import CommandTester
from pytest_mock import MockerFixture
from poetry.utils.env import VirtualEnv
from tests.helpers import TestRepository from tests.helpers import TestRepository
from tests.types import CommandTesterFactory from tests.types import CommandTesterFactory
FIXTURES = Path(__file__).parent.joinpath("fixtures") FIXTURES = Path(__file__).parent.joinpath("fixtures")
@pytest.fixture
def installed_repository() -> Repository:
return Repository()
@pytest.fixture(autouse=True)
def save_environ(environ: None) -> Repository:
yield
@pytest.fixture(autouse=True)
def setup_mocks(
mocker: MockerFixture,
tmp_venv: VirtualEnv,
installed_repository: Repository,
http: type[httpretty.httpretty],
):
mocker.patch.object(EnvManager, "get_system_env", return_value=tmp_venv)
mocker.patch("poetry.installation.executor.pip_install")
mocker.patch(
"poetry.installation.installer.Installer._get_installed",
return_value=installed_repository,
)
@pytest.fixture() @pytest.fixture()
def tester(command_tester_factory: CommandTesterFactory) -> CommandTester: def tester(command_tester_factory: CommandTesterFactory) -> CommandTester:
return command_tester_factory("self update") return command_tester_factory("self update")
...@@ -60,7 +29,7 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester: ...@@ -60,7 +29,7 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester:
def test_self_update_can_update_from_recommended_installation( def test_self_update_can_update_from_recommended_installation(
tester: CommandTester, tester: CommandTester,
repo: TestRepository, repo: TestRepository,
installed_repository: TestRepository, installed: TestRepository,
): ):
new_version = Version.parse(__version__).next_minor().text new_version = Version.parse(__version__).next_minor().text
...@@ -70,8 +39,8 @@ def test_self_update_can_update_from_recommended_installation( ...@@ -70,8 +39,8 @@ def test_self_update_can_update_from_recommended_installation(
new_poetry = Package("poetry", new_version) new_poetry = Package("poetry", new_version)
new_poetry.add_dependency(Factory.create_dependency("cleo", "^1.0.0")) new_poetry.add_dependency(Factory.create_dependency("cleo", "^1.0.0"))
installed_repository.add_package(old_poetry) installed.add_package(old_poetry)
installed_repository.add_package(Package("cleo", "0.8.2")) installed.add_package(Package("cleo", "0.8.2"))
repo.add_package(new_poetry) repo.add_package(new_poetry)
repo.add_package(Package("cleo", "1.0.0")) repo.add_package(Package("cleo", "1.0.0"))
......
from __future__ import annotations
from pathlib import Path
from typing import TYPE_CHECKING
from poetry.factory import Factory
if TYPE_CHECKING:
from tomlkit.container import Table as TOMLTable
def get_self_command_dependencies(locked: bool = True) -> TOMLTable:
from poetry.console.commands.self.self_command import SelfCommand
from poetry.locations import CONFIG_DIR
system_pyproject_file = SelfCommand.get_default_system_pyproject_file()
assert system_pyproject_file.exists()
assert system_pyproject_file.parent == Path(CONFIG_DIR)
if locked:
assert system_pyproject_file.parent.joinpath("poetry.lock").exists()
poetry = Factory().create_poetry(system_pyproject_file.parent, disable_plugins=True)
content = poetry.file.read()["tool"]["poetry"]
assert "group" in content
assert SelfCommand.ADDITIONAL_PACKAGE_GROUP in content["group"]
assert "dependencies" in content["group"][SelfCommand.ADDITIONAL_PACKAGE_GROUP]
return content["group"][SelfCommand.ADDITIONAL_PACKAGE_GROUP]["dependencies"]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment