Skip to content

Commit

Permalink
cli: introduce self add/remove/install commands
Browse files Browse the repository at this point in the history
  • Loading branch information
abn committed Apr 14, 2022
1 parent 9b18eec commit 8d92477
Show file tree
Hide file tree
Showing 10 changed files with 257 additions and 280 deletions.
3 changes: 3 additions & 0 deletions src/poetry/console/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ def _load() -> type[Command]:
"plugin remove",
"plugin show",
# Self commands
"self add",
"self install",
"self remove",
"self update",
# Source commands
"source add",
Expand Down
11 changes: 7 additions & 4 deletions src/poetry/console/commands/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,7 @@ class AddCommand(InstallerCommand, InitCommand):
),
option("lock", None, "Do not perform operations (only update the lockfile)."),
]
help = """\
The add command adds required packages to your <comment>pyproject.toml</> and installs\
them.
examples = """\
If you do not specify a version constraint, poetry will choose a suitable one based on\
the available package versions.
Expand All @@ -81,6 +78,12 @@ class AddCommand(InstallerCommand, InitCommand):
- 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>)
"""
help = f"""\
The add command adds required packages to your <comment>pyproject.toml</> and installs\
them.
{examples}
"""

loggers = ["poetry.repositories.pypi_repository", "poetry.inspection.info"]
Expand Down
4 changes: 4 additions & 0 deletions src/poetry/console/commands/group_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ def non_optional_groups(self) -> set[str]:
if not group.is_optional()
}

@property
def default_group(self) -> str:
return "default"

@property
def activated_groups(self) -> set[str]:
groups = {}
Expand Down
6 changes: 1 addition & 5 deletions src/poetry/console/commands/remove.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,7 @@ def handle(self) -> int:
)
self._installer.set_locker(self.poetry.locker)

# Update packages
self._installer.use_executor(
self.poetry.config.get("experimental.new-installer", False)
)

self._installer.set_package(self.poetry.package)
self._installer.dry_run(self.option("dry-run", False))
self._installer.verbose(self._io.is_verbose())
self._installer.update(True)
Expand Down
22 changes: 22 additions & 0 deletions src/poetry/console/commands/self/add.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from __future__ import annotations

from poetry.console.commands.add import AddCommand
from poetry.console.commands.self.self_command import SelfCommand


class SelfAddCommand(SelfCommand, AddCommand):
name = "self add"
description = "Add additional packages to Poetry's runtime environment."
options = [
o
for o in AddCommand.options
if o.name in {"editable", "extras", "source", "dry-run", "allow-prereleases"}
]
help = f"""\
The <c1>self add</c1> command installs additional package's to Poetry's runtime \
environment.
This is managed in the <comment>{str(SelfCommand.system_pyproject)}</> file.
{AddCommand.examples}
"""
23 changes: 23 additions & 0 deletions src/poetry/console/commands/self/install.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from __future__ import annotations

from poetry.console.commands.install import InstallCommand
from poetry.console.commands.self.self_command import SelfCommand


class SelfInstallCommand(SelfCommand, InstallCommand):
name = "self install"
description = "Add additional packages to Poetry's runtime environment."
options = [o for o in InstallCommand.options if o.name in {"sync", "dry-run"}]
help = f"""\
The <c1>self install</c1> command all additional packages specified are installed in \
the current runtime environment.
This is managed in the <comment>{str(SelfCommand.system_pyproject)}</> file.
You can add more packages using the <c1>self add</c1> command and remove them using \
the <c1>self remove</c1> command.
"""

@property
def activated_groups(self) -> set[str]:
return {"default", self.default_group}
16 changes: 16 additions & 0 deletions src/poetry/console/commands/self/remove.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from __future__ import annotations

from poetry.console.commands.remove import RemoveCommand
from poetry.console.commands.self.self_command import SelfCommand


class SelfRemoveCommand(SelfCommand, RemoveCommand):
name = "self remove"
description = "Remove additional packages from Poetry's runtime environment."
options = [o for o in RemoveCommand.options if o.name in {"dry-run"}]
help = f"""\
The <c1>self remove</c1> command removes additional package's to Poetry's runtime \
environment.
This is managed in the <comment>{str(SelfCommand.system_pyproject)}</> file.
"""
107 changes: 107 additions & 0 deletions src/poetry/console/commands/self/self_command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from __future__ import annotations

import os

from contextlib import contextmanager
from pathlib import Path
from typing import TYPE_CHECKING

from poetry.core.packages.dependency import Dependency
from poetry.core.pyproject.toml import PyProjectTOML

from poetry.__version__ import __version__
from poetry.console.commands.installer_command import InstallerCommand
from poetry.factory import Factory
from poetry.packages.project_package import ProjectPackage
from poetry.utils.env import EnvManager
from poetry.utils.env import SystemEnv


if TYPE_CHECKING:
from typing import Iterator

from poetry.poetry import Poetry
from poetry.utils.env import Env


class SelfCommand(InstallerCommand):
@property
def system_pyproject(self) -> Path:
from poetry.locations import CONFIG_DIR

config_dir = Path(CONFIG_DIR)
config_dir.mkdir(parents=True, exist_ok=True)

return config_dir.joinpath("pyproject.toml")

def reset_env(self) -> None:
self._env = EnvManager.get_system_env(naive=True)

@property
def env(self) -> Env:
if self._env is None or not isinstance(self._env, SystemEnv):
self.reset_env()
return self._env

@property
def default_group(self) -> str:
return "additional"

@property
def activated_groups(self) -> set[str]:
return {self.default_group}

@contextmanager
def with_system_project(self) -> Iterator[Path]:
cwd = Path.cwd()
try:
os.chdir(self.system_pyproject.parent)
yield self.system_pyproject.parent
finally:
os.chdir(cwd)

def generate_system_pyproject(self) -> None:
preserved = {}

if self.system_pyproject.exists():
content = PyProjectTOML(self.system_pyproject).poetry_config

for key in {"group", "source"}:
if key in content:
preserved[key] = content[key]

package = ProjectPackage(name="poetry-instance", version=__version__)
package.add_dependency(Dependency(name="poetry", constraint=__version__))

package.python_versions = ".".join(str(v) for v in self.env.version_info[:3])

content = Factory.create_pyproject_from_package(package=package)

for key in preserved:
content[key] = preserved[key]

self.system_pyproject.write_text(content.as_string(), encoding="utf-8")

def reset_poetry(self) -> None:
with self.with_system_project():
self.generate_system_pyproject()
self._poetry = Factory().create_poetry(
self.system_pyproject.parent, io=self._io, disable_plugins=True
)

@property
def poetry(self) -> Poetry:
if self._poetry is None:
self.reset_poetry()
assert self._poetry
return self._poetry

def _system_project_handle(self) -> int:
return super().handle()

def handle(self) -> int:
self.reset_env()
self.reset_poetry()

with self.with_system_project():
return self._system_project_handle()
Loading

0 comments on commit 8d92477

Please sign in to comment.