-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Replace the export command by the export plugin #4824
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,7 +53,6 @@ def _load() -> Type[Command]: | |
"build", | ||
"check", | ||
"config", | ||
"export", | ||
"init", | ||
"install", | ||
"lock", | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -377,16 +377,11 @@ def _find_best_version_for_package( | |
return package.pretty_name, selector.find_recommended_require_version(package) | ||
|
||
def _parse_requirements(self, requirements: List[str]) -> List[Dict[str, str]]: | ||
from poetry.core.pyproject.exceptions import PyProjectException | ||
|
||
from poetry.puzzle.provider import Provider | ||
|
||
result = [] | ||
|
||
try: | ||
cwd = self.poetry.file.parent | ||
except (PyProjectException, RuntimeError): | ||
cwd = Path.cwd() | ||
cwd = Path.cwd() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks unrelated. I'm all for simplification, but could we explain this change/break it out into a separate commit? |
||
|
||
for requirement in requirements: | ||
requirement = requirement.strip() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,11 @@ | ||
import os | ||
|
||
from typing import Dict | ||
from typing import List | ||
from typing import cast | ||
|
||
from cleo.helpers import argument | ||
from cleo.helpers import option | ||
|
||
from poetry.console.application import Application | ||
from poetry.console.commands.init import InitCommand | ||
from poetry.console.commands.update import UpdateCommand | ||
from poetry.console.commands.plugin.plugin_command_mixin import PluginCommandMixin | ||
|
||
|
||
class PluginAddCommand(InitCommand): | ||
class PluginAddCommand(InitCommand, PluginCommandMixin): | ||
|
||
name = "plugin add" | ||
|
||
|
@@ -51,80 +44,69 @@ class PluginAddCommand(InitCommand): | |
""" | ||
|
||
def handle(self) -> int: | ||
from pathlib import Path | ||
|
||
import tomlkit | ||
|
||
from cleo.io.inputs.string_input import StringInput | ||
from cleo.io.io import IO | ||
from poetry.core.pyproject.toml import PyProjectTOML | ||
from poetry.core.semver.helpers import parse_constraint | ||
|
||
from poetry.factory import Factory | ||
from poetry.packages.project_package import ProjectPackage | ||
from poetry.repositories.installed_repository import InstalledRepository | ||
from poetry.locations import home_dir | ||
from poetry.utils.env import EnvManager | ||
from poetry.utils.helpers import canonicalize_name | ||
|
||
plugins = self.argument("plugins") | ||
requested_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") if os.getenv("POETRY_HOME") else system_env.path | ||
) | ||
env_dir = home_dir() | ||
|
||
# We check for the plugins existence first. | ||
if env_dir.joinpath("pyproject.toml").exists(): | ||
pyproject = 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" | ||
existing_plugins = {} | ||
if env_dir.joinpath("plugins.toml").exists(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do think a separate file looks easier. If I'm reading this right, this is a manifest of known plugins in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This entire file (and most of these codepaths) are long, imperative, and repeat a fair bit. We're also sprinkling |
||
existing_plugins = tomlkit.loads( | ||
env_dir.joinpath("plugins.toml").read_text(encoding="utf-8") | ||
) | ||
|
||
if existing_packages: | ||
self.notify_about_existing_packages(existing_packages) | ||
root_package = self.create_env_package(system_env, existing_plugins) | ||
|
||
plugins = [plugin for plugin in plugins if plugin not in existing_packages] | ||
installed_plugins = { | ||
canonicalize_name(ep.distro.name) for ep in self.get_plugin_entry_points() | ||
} | ||
|
||
if not plugins: | ||
return 0 | ||
# We check for the plugins existence first. | ||
plugins = [] | ||
skipped_plugins = [] | ||
parsed_plugins = self._parse_requirements(requested_plugins) | ||
for i, plugin in enumerate(parsed_plugins): | ||
plugin_name = canonicalize_name(plugin.pop("name")) | ||
if plugin_name in installed_plugins: | ||
if plugin_name not in existing_plugins: | ||
existing_plugins[plugin_name] = plugin | ||
|
||
if not plugin: | ||
skipped_plugins.append(plugin_name) | ||
|
||
plugins = self._determine_requirements(plugins) | ||
continue | ||
|
||
# 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 taken into account when resolving dependencies. | ||
installed_repository = InstalledRepository.load( | ||
system_env, with_dependencies=True | ||
) | ||
plugins.append(canonicalize_name(requested_plugins[i])) | ||
|
||
root_package = None | ||
for package in installed_repository.packages: | ||
if package.name == "poetry": | ||
root_package = ProjectPackage(package.name, package.version) | ||
for dependency in package.requires: | ||
root_package.add_dependency(dependency) | ||
if skipped_plugins: | ||
self.line( | ||
"The following plugins are already present and will be skipped:\n" | ||
) | ||
for name in sorted(skipped_plugins): | ||
self.line(f" • <c1>{name}</c1>") | ||
|
||
break | ||
self.line( | ||
"\nIf you want to upgrade it to the latest compatible version, " | ||
"you can use `poetry plugin add plugin@latest.\n" | ||
sdispater marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
|
||
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 = PyProjectTOML(env_dir.joinpath("pyproject.toml")) | ||
poetry_content = pyproject.poetry_config | ||
poetry_dependency_section = poetry_content["dependencies"] | ||
if not plugins: | ||
return 0 | ||
|
||
plugins = self._determine_requirements(plugins) | ||
|
||
# We add the plugins to the plugins.toml file | ||
plugin_names = [] | ||
for plugin in plugins: | ||
if "version" in plugin: | ||
|
@@ -141,57 +123,22 @@ def handle(self) -> int: | |
if len(constraint) == 1 and "version" in constraint: | ||
constraint = constraint["version"] | ||
|
||
poetry_dependency_section[plugin["name"]] = constraint | ||
plugin_names.append(plugin["name"]) | ||
|
||
pyproject.save() | ||
|
||
# From this point forward, all the logic will be deferred to | ||
# 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") | ||
|
||
return update_command.run( | ||
IO( | ||
StringInput(" ".join(argv)), | ||
self._io.output, | ||
self._io.error_output, | ||
root_package.add_dependency( | ||
Factory.create_dependency(plugin["name"], constraint) | ||
) | ||
) | ||
|
||
def get_existing_packages_from_input( | ||
self, packages: List[str], poetry_content: Dict, target_section: str | ||
) -> List[str]: | ||
existing_packages = [] | ||
existing_plugins[plugin["name"]] = constraint | ||
plugin_names.append(plugin["name"]) | ||
|
||
for name in packages: | ||
for key in poetry_content[target_section]: | ||
if key.lower() == name.lower(): | ||
existing_packages.append(name) | ||
return_code = self.update( | ||
system_env, root_package, self._io, whitelist=plugin_names | ||
) | ||
|
||
return existing_packages | ||
if return_code != 0 or self.option("dry-run"): | ||
return return_code | ||
|
||
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" | ||
env_dir.joinpath("plugins.toml").write_text( | ||
tomlkit.dumps(existing_plugins, sort_keys=True), encoding="utf-8" | ||
) | ||
|
||
return 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious -- does the export plugin require the current changes in core? If not I'd like to move this change to a new PR as it affects other PRs and changes (e.g. unblocks #4743).