"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "poetry/console/commands/add.py" between
poetry-1.1.15.tar.gz and poetry-1.2.0.tar.gz

About: Poetry is a tool for dependency management and packaging in Python.

add.py  (poetry-1.1.15):add.py  (poetry-1.2.0)
# -*- coding: utf-8 -*- from __future__ import annotations
from typing import Dict
from typing import List
from cleo import argument import contextlib
from cleo import option
from .init import InitCommand from typing import Any
from .installer_command import InstallerCommand
class AddCommand(InstallerCommand, InitCommand): from cleo.helpers import argument
from cleo.helpers import option
from packaging.utils import canonicalize_name
from poetry.core.packages.dependency_group import MAIN_GROUP
from tomlkit.toml_document import TOMLDocument
from poetry.console.commands.init import InitCommand
from poetry.console.commands.installer_command import InstallerCommand
class AddCommand(InstallerCommand, InitCommand):
name = "add" name = "add"
description = "Adds a new dependency to <comment>pyproject.toml</>." description = "Adds a new dependency to <comment>pyproject.toml</>."
arguments = [argument("name", "The packages to add.", multiple=True)] arguments = [argument("name", "The packages to add.", multiple=True)]
options = [ options = [
option(
"group",
"-G",
"The group to add the dependency to.",
flag=False,
default=MAIN_GROUP,
),
option("dev", "D", "Add as a development dependency."), option("dev", "D", "Add as a development dependency."),
option("editable", "e", "Add vcs/path dependencies as editable."),
option( option(
"extras", "extras",
"E", "E",
"Extras to activate for the dependency.", "Extras to activate for the dependency.",
flag=False, flag=False,
multiple=True, multiple=True,
), ),
option("optional", None, "Add as an optional dependency."), option("optional", None, "Add as an optional dependency."),
option( option(
"python", "python",
skipping to change at line 49 skipping to change at line 61
option( option(
"source", "source",
None, None,
"Name of the source to use to install the package.", "Name of the source to use to install the package.",
flag=False, flag=False,
), ),
option("allow-prereleases", None, "Accept prereleases."), option("allow-prereleases", None, "Accept prereleases."),
option( option(
"dry-run", "dry-run",
None, None,
"Output the operations but do not execute anything (implicitly enabl "Output the operations but do not execute anything (implicitly enabl
es --verbose).", es"
" --verbose).",
), ),
option("lock", None, "Do not perform operations (only update the lockfil e)."), option("lock", None, "Do not perform operations (only update the lockfil e)."),
] ]
help = ( examples = """\
"The add command adds required packages to your <comment>pyproject.toml< If you do not specify a version constraint, poetry will choose a suitable one ba
/> and installs them.\n\n" sed on\
"If you do not specify a version constraint, poetry will choose a suitab the available package versions.
le one based on the available package versions.\n\n"
"You can specify a package in the following forms:\n" You can specify a package in the following forms:
" - A single name (<b>requests</b>)\n" - A single name (<b>requests</b>)
" - A name and a constraint (<b>requests@^2.23.0</b>)\n" - 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 (<b>git+https://github.com/python-poetry/poetry.git</b>)
n" - A git url with a revision\
" - A git url with a revision (<b>git+https://github.com/python-poetry/ (<b>git+https://github.com/python-poetry/poetry.git#develop</b>)
poetry.git#develop</b>)\n" - A git SSH url (<b>git+ssh://github.com/python-poetry/poetry.git</b>)
" - A file path (<b>../my-package/my-package.whl</b>)\n" - A git SSH url with a revision\
" - A directory (<b>../my-package/</b>)\n" (<b>git+ssh://github.com/python-poetry/poetry.git#develop</b>)
" - A url (<b>https://example.com/packages/my-package-0.1.0.tar.gz</b>) - A file path (<b>../my-package/my-package.whl</b>)
\n" - 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 in
stalls\
them.
{examples}
"""
loggers = ["poetry.repositories.pypi_repository", "poetry.inspection.info"] loggers = ["poetry.repositories.pypi_repository", "poetry.inspection.info"]
def handle(self): def handle(self) -> int:
from poetry.core.semver.helpers import parse_constraint
from tomlkit import inline_table from tomlkit import inline_table
from tomlkit import parse as parse_toml
from tomlkit import table
from poetry.core.semver import parse_constraint from poetry.factory import Factory
packages = self.argument("name") packages = self.argument("name")
is_dev = self.option("dev") if self.option("dev"):
self.line_error(
"<warning>The --dev option is deprecated, "
"use the `--group dev` notation instead.</warning>"
)
group = "dev"
else:
group = self.option("group", self.default_group or MAIN_GROUP)
if self.option("extras") and len(packages) > 1: if self.option("extras") and len(packages) > 1:
raise ValueError( raise ValueError(
"You can only specify one package " "when using the --extras opt ion" "You can only specify one package when using the --extras option "
) )
section = "dependencies" # tomlkit types are awkward to work with, treat content as a mostly unty
if is_dev: ped
section = "dev-dependencies" # dictionary.
content: dict[str, Any] = self.poetry.file.read()
original_content = self.poetry.file.read()
content = self.poetry.file.read()
poetry_content = content["tool"]["poetry"] poetry_content = content["tool"]["poetry"]
if section not in poetry_content: if group == MAIN_GROUP:
poetry_content[section] = {} if "dependencies" not in poetry_content:
poetry_content["dependencies"] = table()
section = poetry_content["dependencies"]
else:
if "group" not in poetry_content:
poetry_content["group"] = table(is_super_table=True)
groups = poetry_content["group"]
if group not in groups:
dependencies_toml: dict[str, Any] = parse_toml(
f"[tool.poetry.group.{group}.dependencies]\n\n"
)
group_table = dependencies_toml["tool"]["poetry"]["group"][group
]
poetry_content["group"][group] = group_table
existing_packages = self.get_existing_packages_from_input( if "dependencies" not in poetry_content["group"][group]:
packages, poetry_content, section poetry_content["group"][group]["dependencies"] = table()
)
section = poetry_content["group"][group]["dependencies"]
existing_packages = self.get_existing_packages_from_input(packages, sect
ion)
if existing_packages: if existing_packages:
self.notify_about_existing_packages(existing_packages) self.notify_about_existing_packages(existing_packages)
packages = [name for name in packages if name not in existing_packages] packages = [name for name in packages if name not in existing_packages]
if not packages: if not packages:
self.line("Nothing to add.") self.line("Nothing to add.")
return 0 return 0
requirements = self._determine_requirements( requirements = self._determine_requirements(
packages, packages,
allow_prereleases=self.option("allow-prereleases"), allow_prereleases=self.option("allow-prereleases"),
source=self.option("source"), source=self.option("source"),
) )
for _constraint in requirements: for _constraint in requirements:
if "version" in _constraint: version = _constraint.get("version")
if version is not None:
# Validate version constraint # Validate version constraint
parse_constraint(_constraint["version"]) assert isinstance(version, str)
parse_constraint(version)
constraint = inline_table() constraint: dict[str, Any] = inline_table()
for name, value in _constraint.items(): for name, value in _constraint.items():
if name == "name": if name == "name":
continue continue
constraint[name] = value constraint[name] = value
if self.option("optional"): if self.option("optional"):
constraint["optional"] = True constraint["optional"] = True
if self.option("allow-prereleases"): if self.option("allow-prereleases"):
skipping to change at line 139 skipping to change at line 189
if self.option("extras"): if self.option("extras"):
extras = [] extras = []
for extra in self.option("extras"): for extra in self.option("extras"):
if " " in extra: if " " in extra:
extras += [e.strip() for e in extra.split(" ")] extras += [e.strip() for e in extra.split(" ")]
else: else:
extras.append(extra) extras.append(extra)
constraint["extras"] = self.option("extras") constraint["extras"] = self.option("extras")
if self.option("editable"):
if "git" in _constraint or "path" in _constraint:
constraint["develop"] = True
else:
self.line_error(
"\n"
"<error>Failed to add packages. "
"Only vcs/path dependencies support editable installs. "
f"<c1>{_constraint['name']}</c1> is neither."
)
self.line_error("\nNo changes were applied.")
return 1
if self.option("python"): if self.option("python"):
constraint["python"] = self.option("python") constraint["python"] = self.option("python")
if self.option("platform"): if self.option("platform"):
constraint["platform"] = self.option("platform") constraint["platform"] = self.option("platform")
if self.option("source"): if self.option("source"):
constraint["source"] = self.option("source") constraint["source"] = self.option("source")
if len(constraint) == 1 and "version" in constraint: if len(constraint) == 1 and "version" in constraint:
constraint = constraint["version"] constraint = constraint["version"]
poetry_content[section][_constraint["name"]] = constraint constraint_name = _constraint["name"]
assert isinstance(constraint_name, str)
section[constraint_name] = constraint
with contextlib.suppress(ValueError):
self.poetry.package.dependency_group(group).remove_dependency(
constraint_name
)
try: self.poetry.package.add_dependency(
# Write new content Factory.create_dependency(
self.poetry.file.write(content) constraint_name,
constraint,
groups=[group],
root_dir=self.poetry.file.parent,
)
)
# Cosmetic new line # Refresh the locker
self.line("") self.poetry.set_locker(
self.poetry.locker.__class__(self.poetry.locker.lock.path, poetry_co
ntent)
)
self.installer.set_locker(self.poetry.locker)
# Update packages # Cosmetic new line
self.reset_poetry() self.line("")
self._installer.set_package(self.poetry.package) self.installer.set_package(self.poetry.package)
self._installer.dry_run(self.option("dry-run")) self.installer.dry_run(self.option("dry-run"))
self._installer.verbose(self._io.is_verbose()) self.installer.verbose(self.io.is_verbose())
self._installer.update(True) self.installer.update(True)
if self.option("lock"): if self.option("lock"):
self._installer.lock() self.installer.lock()
self._installer.whitelist([r["name"] for r in requirements]) self.installer.whitelist([r["name"] for r in requirements])
status = self._installer.run()
except BaseException:
# Using BaseException here as some exceptions, eg: KeyboardInterrupt
, do not inherit from Exception
self.poetry.file.write(original_content)
raise
if status != 0 or self.option("dry-run"):
# Revert changes
if not self.option("dry-run"):
self.line_error(
"\n"
"<error>Failed to add packages, reverting the pyproject.toml
file "
"to its original content.</error>"
)
self.poetry.file.write(original_content) status = self.installer.run()
if status == 0 and not self.option("dry-run"):
assert isinstance(content, TOMLDocument)
self.poetry.file.write(content)
return status return status
def get_existing_packages_from_input( def get_existing_packages_from_input(
self, packages, poetry_content, target_section self, packages: list[str], section: dict[str, Any]
): # type: (List[str], Dict, str) -> List[str] ) -> list[str]:
existing_packages = [] existing_packages = []
for name in packages: for name in packages:
for key in poetry_content[target_section]: for key in section:
if key.lower() == name.lower(): if canonicalize_name(key) == canonicalize_name(name):
existing_packages.append(name) existing_packages.append(name)
return existing_packages return existing_packages
def notify_about_existing_packages( @property
self, existing_packages def _hint_update_packages(self) -> str:
): # type: (List[str]) -> None return (
self.line( "\nIf you want to update it to the latest compatible version, you ca
"The following packages are already present in the pyproject.toml an n use"
d will be skipped:\n" " `poetry update package`.\nIf you prefer to upgrade it to the lates
t"
" available version, you can use `poetry add package@latest`.\n"
) )
for name in existing_packages:
self.line(" • <c1>{name}</c1>".format(name=name)) def notify_about_existing_packages(self, existing_packages: list[str]) -> No
ne:
self.line( self.line(
"\nIf you want to update it to the latest compatible version, you ca "The following packages are already present in the pyproject.toml an
n use `poetry update package`.\n" d will"
"If you prefer to upgrade it to the latest available version, you ca " be skipped:\n"
n use `poetry add package@latest`.\n"
) )
for name in existing_packages:
self.line(f" • <c1>{name}</c1>")
self.line(self._hint_update_packages)
 End of changes. 33 change blocks. 
95 lines changed or deleted 163 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)