show.py (poetry-1.1.15) | : | show.py (poetry-1.2.0) | ||
---|---|---|---|---|
# -*- coding: utf-8 -*- | from __future__ import annotations | |||
from cleo import argument | ||||
from cleo import option | ||||
from .env_command import EnvCommand | from typing import TYPE_CHECKING | |||
class ShowCommand(EnvCommand): | from cleo.helpers import argument | |||
from cleo.helpers import option | ||||
from packaging.utils import canonicalize_name | ||||
from poetry.console.commands.group_command import GroupCommand | ||||
if TYPE_CHECKING: | ||||
from cleo.io.io import IO | ||||
from packaging.utils import NormalizedName | ||||
from poetry.core.packages.dependency import Dependency | ||||
from poetry.core.packages.package import Package | ||||
from poetry.core.packages.project_package import ProjectPackage | ||||
from poetry.repositories.repository import Repository | ||||
def reverse_deps(pkg: Package, repo: Repository) -> dict[str, str]: | ||||
required_by = {} | ||||
for locked in repo.packages: | ||||
dependencies = {d.name: d.pretty_constraint for d in locked.requires} | ||||
if pkg.name in dependencies: | ||||
required_by[locked.pretty_name] = dependencies[pkg.name] | ||||
return required_by | ||||
class ShowCommand(GroupCommand): | ||||
name = "show" | name = "show" | |||
description = "Shows information about packages." | description = "Shows information about packages." | |||
arguments = [argument("package", "The package to inspect", optional=True)] | arguments = [argument("package", "The package to inspect", optional=True)] | |||
options = [ | options = [ | |||
option("no-dev", None, "Do not list the development dependencies."), | *GroupCommand._group_dependency_options(), | |||
option( | ||||
"no-dev", | ||||
None, | ||||
"Do not list the development dependencies. (<warning>Deprecated</war | ||||
ning>)", | ||||
), | ||||
option("tree", "t", "List the dependencies as a tree."), | option("tree", "t", "List the dependencies as a tree."), | |||
option( | ||||
"why", | ||||
None, | ||||
"When showing the full list, or a <info>--tree</info> for a single p | ||||
ackage," | ||||
" also display why it's included.", | ||||
), | ||||
option("latest", "l", "Show the latest version."), | option("latest", "l", "Show the latest version."), | |||
option( | option( | |||
"outdated", | "outdated", | |||
"o", | "o", | |||
"Show the latest version but only for packages that are outdated.", | "Show the latest version but only for packages that are outdated.", | |||
), | ), | |||
option( | option( | |||
"all", | "all", | |||
"a", | "a", | |||
"Show all packages (even those not compatible with current system)." , | "Show all packages (even those not compatible with current system)." , | |||
), | ), | |||
] | ] | |||
help = """The show command displays detailed information about a package, or | help = """The show command displays detailed information about a package, or | |||
lists all packages available.""" | lists all packages available.""" | |||
colors = ["cyan", "yellow", "green", "magenta", "blue"] | colors = ["cyan", "yellow", "green", "magenta", "blue"] | |||
def handle(self): | def handle(self) -> int: | |||
from clikit.utils.terminal import Terminal | from cleo.io.null_io import NullIO | |||
from cleo.terminal import Terminal | ||||
from poetry.io.null_io import NullIO | ||||
from poetry.puzzle.solver import Solver | from poetry.puzzle.solver import Solver | |||
from poetry.repositories.installed_repository import InstalledRepository | from poetry.repositories.installed_repository import InstalledRepository | |||
from poetry.repositories.pool import Pool | from poetry.repositories.pool import Pool | |||
from poetry.repositories.repository import Repository | ||||
from poetry.utils.helpers import get_package_version_display_string | from poetry.utils.helpers import get_package_version_display_string | |||
package = self.argument("package") | package = self.argument("package") | |||
if self.option("tree"): | if self.option("tree"): | |||
self.init_styles(self.io) | self.init_styles(self.io) | |||
if self.option("why"): | ||||
if self.option("tree") and package is None: | ||||
self.line_error( | ||||
"<error>Error: --why requires a package when combined with" | ||||
" --tree.</error>" | ||||
) | ||||
return 1 | ||||
if not self.option("tree") and package: | ||||
self.line_error( | ||||
"<error>Error: --why cannot be used without --tree when disp | ||||
laying" | ||||
" a single package.</error>" | ||||
) | ||||
return 1 | ||||
if self.option("outdated"): | if self.option("outdated"): | |||
self._args.set_option("latest", True) | self.io.input.set_option("latest", True) | |||
include_dev = not self.option("no-dev") | if not self.poetry.locker.is_locked(): | |||
locked_repo = self.poetry.locker.locked_repository(True) | self.line_error( | |||
"<error>Error: poetry.lock not found. Run `poetry lock` to creat | ||||
e" | ||||
" it.</error>" | ||||
) | ||||
return 1 | ||||
locked_repo = self.poetry.locker.locked_repository() | ||||
root = self.project_with_activated_groups_only() | ||||
# Show tree view if requested | # Show tree view if requested | |||
if self.option("tree") and not package: | if self.option("tree") and package is None: | |||
requires = self.poetry.package.requires | requires = root.all_requires | |||
if include_dev: | ||||
requires += self.poetry.package.dev_requires | ||||
packages = locked_repo.packages | packages = locked_repo.packages | |||
for package in packages: | for p in packages: | |||
for require in requires: | for require in requires: | |||
if package.name == require.name: | if p.name == require.name: | |||
self.display_package_tree(self._io, package, locked_repo | self.display_package_tree(self.io, p, packages) | |||
) | ||||
break | break | |||
return 0 | return 0 | |||
table = self.table(style="compact") | table = self.table(style="compact") | |||
# table.style.line_vc_char = "" | ||||
locked_packages = locked_repo.packages | locked_packages = locked_repo.packages | |||
pool = Pool(ignore_repository_names=True) | pool = Pool(ignore_repository_names=True) | |||
pool.add_repository(locked_repo) | pool.add_repository(locked_repo) | |||
solver = Solver( | solver = Solver( | |||
self.poetry.package, | root, | |||
pool=pool, | pool=pool, | |||
installed=Repository(), | installed=[], | |||
locked=locked_repo, | locked=locked_packages, | |||
io=NullIO(), | io=NullIO(), | |||
) | ) | |||
solver.provider.load_deferred(False) | solver.provider.load_deferred(False) | |||
with solver.use_environment(self.env): | with solver.use_environment(self.env): | |||
ops = solver.solve() | ops = solver.solve().calculate_operations() | |||
required_locked_packages = set([op.package for op in ops if not op.skipp | ||||
ed]) | ||||
if self.option("no-dev"): | required_locked_packages = {op.package for op in ops if not op.skipped} | |||
required_locked_packages = [ | ||||
p for p in locked_packages if p.category == "main" | ||||
] | ||||
if package: | if package: | |||
pkg = None | pkg = None | |||
for locked in locked_packages: | for locked in locked_packages: | |||
if package.lower() == locked.name: | if canonicalize_name(package) == locked.name: | |||
pkg = locked | pkg = locked | |||
break | break | |||
if not pkg: | if not pkg: | |||
raise ValueError("Package {} not found".format(package)) | raise ValueError(f"Package {package} not found") | |||
required_by = reverse_deps(pkg, locked_repo) | ||||
if self.option("tree"): | if self.option("tree"): | |||
self.display_package_tree(self.io, pkg, locked_repo) | if self.option("why"): | |||
# The default case if there's no reverse dependencies is to | ||||
query | ||||
# the subtree for pkg but if any rev-deps exist we'll query | ||||
for each | ||||
# of them in turn | ||||
packages = [pkg] | ||||
if required_by: | ||||
packages = [ | ||||
p | ||||
for p in locked_packages | ||||
for r in required_by.keys() | ||||
if p.name == r | ||||
] | ||||
else: | ||||
# if no rev-deps exist we'll make this clear as it can o | ||||
therwise | ||||
# look very odd for packages that also have no or few di | ||||
rect | ||||
# dependencies | ||||
self.io.write_line(f"Package {package} is a direct depen | ||||
dency.") | ||||
for p in packages: | ||||
self.display_package_tree( | ||||
self.io, p, locked_packages, why_package=pkg | ||||
) | ||||
else: | ||||
self.display_package_tree(self.io, pkg, locked_packages) | ||||
return 0 | return 0 | |||
rows = [ | rows = [ | |||
["<info>name</>", " : <c1>{}</>".format(pkg.pretty_name)], | ["<info>name</>", f" : <c1>{pkg.pretty_name}</>"], | |||
["<info>version</>", " : <b>{}</b>".format(pkg.pretty_version)], | ["<info>version</>", f" : <b>{pkg.pretty_version}</b>"], | |||
["<info>description</>", " : {}".format(pkg.description)], | ["<info>description</>", f" : {pkg.description}"], | |||
] | ] | |||
table.add_rows(rows) | table.add_rows(rows) | |||
table.render(self.io) | table.render() | |||
if pkg.requires: | if pkg.requires: | |||
self.line("") | self.line("") | |||
self.line("<info>dependencies</info>") | self.line("<info>dependencies</info>") | |||
for dependency in pkg.requires: | for dependency in pkg.requires: | |||
self.line( | self.line( | |||
" - <c1>{}</c1> <b>{}</b>".format( | f" - <c1>{dependency.pretty_name}</c1>" | |||
dependency.pretty_name, dependency.pretty_constraint | f" <b>{dependency.pretty_constraint}</b>" | |||
) | ||||
) | ) | |||
if required_by: | ||||
self.line("") | ||||
self.line("<info>required by</info>") | ||||
for parent, requires_version in required_by.items(): | ||||
self.line(f" - <c1>{parent}</c1> <b>{requires_version}</b>") | ||||
return 0 | return 0 | |||
show_latest = self.option("latest") | show_latest = self.option("latest") | |||
show_all = self.option("all") | show_all = self.option("all") | |||
terminal = Terminal() | terminal = Terminal() | |||
width = terminal.width | width = terminal.width | |||
name_length = version_length = latest_length = 0 | name_length = version_length = latest_length = required_by_length = 0 | |||
latest_packages = {} | latest_packages = {} | |||
latest_statuses = {} | latest_statuses = {} | |||
installed_repo = InstalledRepository.load(self.env) | installed_repo = InstalledRepository.load(self.env) | |||
# Computing widths | # Computing widths | |||
for locked in locked_packages: | for locked in locked_packages: | |||
if locked not in required_locked_packages and not show_all: | if locked not in required_locked_packages and not show_all: | |||
continue | continue | |||
current_length = len(locked.pretty_name) | current_length = len(locked.pretty_name) | |||
if not self._io.output.supports_ansi(): | if not self.io.output.is_decorated(): | |||
installed_status = self.get_installed_status(locked, installed_r | installed_status = self.get_installed_status( | |||
epo) | locked, installed_repo.packages | |||
) | ||||
if installed_status == "not-installed": | if installed_status == "not-installed": | |||
current_length += 4 | current_length += 4 | |||
if show_latest: | if show_latest: | |||
latest = self.find_latest_package(locked, include_dev) | latest = self.find_latest_package(locked, root) | |||
if not latest: | if not latest: | |||
latest = locked | latest = locked | |||
latest_packages[locked.pretty_name] = latest | latest_packages[locked.pretty_name] = latest | |||
update_status = latest_statuses[ | update_status = latest_statuses[ | |||
locked.pretty_name | locked.pretty_name | |||
] = self.get_update_status(latest, locked) | ] = self.get_update_status(latest, locked) | |||
if not self.option("outdated") or update_status != "up-to-date": | if not self.option("outdated") or update_status != "up-to-date": | |||
name_length = max(name_length, current_length) | name_length = max(name_length, current_length) | |||
skipping to change at line 177 | skipping to change at line 258 | |||
), | ), | |||
) | ) | |||
latest_length = max( | latest_length = max( | |||
latest_length, | latest_length, | |||
len( | len( | |||
get_package_version_display_string( | get_package_version_display_string( | |||
latest, root=self.poetry.file.parent | latest, root=self.poetry.file.parent | |||
) | ) | |||
), | ), | |||
) | ) | |||
if self.option("why"): | ||||
required_by = reverse_deps(locked, locked_repo) | ||||
required_by_length = max( | ||||
required_by_length, | ||||
len(" from " + ",".join(required_by.keys())), | ||||
) | ||||
else: | else: | |||
name_length = max(name_length, current_length) | name_length = max(name_length, current_length) | |||
version_length = max( | version_length = max( | |||
version_length, | version_length, | |||
len( | len( | |||
get_package_version_display_string( | get_package_version_display_string( | |||
locked, root=self.poetry.file.parent | locked, root=self.poetry.file.parent | |||
) | ) | |||
), | ), | |||
) | ) | |||
if self.option("why"): | ||||
required_by = reverse_deps(locked, locked_repo) | ||||
required_by_length = max( | ||||
required_by_length, len(" from " + ",".join(required_by. | ||||
keys())) | ||||
) | ||||
write_version = name_length + version_length + 3 <= width | write_version = name_length + version_length + 3 <= width | |||
write_latest = name_length + version_length + latest_length + 3 <= width | write_latest = name_length + version_length + latest_length + 3 <= width | |||
write_description = name_length + version_length + latest_length + 24 <= | ||||
width | why_end_column = ( | |||
name_length + version_length + latest_length + required_by_length | ||||
) | ||||
write_why = self.option("why") and (why_end_column + 3) <= width | ||||
write_description = (why_end_column + 24) <= width | ||||
for locked in locked_packages: | for locked in locked_packages: | |||
color = "cyan" | color = "cyan" | |||
name = locked.pretty_name | name = locked.pretty_name | |||
install_marker = "" | install_marker = "" | |||
if locked not in required_locked_packages: | if locked not in required_locked_packages: | |||
if not show_all: | if not show_all: | |||
continue | continue | |||
color = "black;options=bold" | color = "black;options=bold" | |||
else: | else: | |||
installed_status = self.get_installed_status(locked, installed_r | installed_status = self.get_installed_status( | |||
epo) | locked, installed_repo.packages | |||
) | ||||
if installed_status == "not-installed": | if installed_status == "not-installed": | |||
color = "red" | color = "red" | |||
if not self._io.output.supports_ansi(): | if not self.io.output.is_decorated(): | |||
# Non installed in non decorated mode | # Non installed in non decorated mode | |||
install_marker = " (!)" | install_marker = " (!)" | |||
if ( | if ( | |||
show_latest | show_latest | |||
and self.option("outdated") | and self.option("outdated") | |||
and latest_statuses[locked.pretty_name] == "up-to-date" | and latest_statuses[locked.pretty_name] == "up-to-date" | |||
): | ): | |||
continue | continue | |||
line = "<fg={}>{:{}}{}</>".format( | line = ( | |||
color, name, name_length - len(install_marker), install_marker | f"<fg={color}>" | |||
f"{name:{name_length - len(install_marker)}}{install_marker}</>" | ||||
) | ) | |||
if write_version: | if write_version: | |||
line += " <b>{:{}}</b>".format( | version = get_package_version_display_string( | |||
get_package_version_display_string( | locked, root=self.poetry.file.parent | |||
locked, root=self.poetry.file.parent | ||||
), | ||||
version_length, | ||||
) | ) | |||
line += f" <b>{version:{version_length}}</b>" | ||||
if show_latest: | if show_latest: | |||
latest = latest_packages[locked.pretty_name] | latest = latest_packages[locked.pretty_name] | |||
update_status = latest_statuses[locked.pretty_name] | update_status = latest_statuses[locked.pretty_name] | |||
if write_latest: | if write_latest: | |||
color = "green" | color = "green" | |||
if update_status == "semver-safe-update": | if update_status == "semver-safe-update": | |||
color = "red" | color = "red" | |||
elif update_status == "update-possible": | elif update_status == "update-possible": | |||
color = "yellow" | color = "yellow" | |||
line += " <fg={}>{:{}}</>".format( | version = get_package_version_display_string( | |||
color, | latest, root=self.poetry.file.parent | |||
get_package_version_display_string( | ||||
latest, root=self.poetry.file.parent | ||||
), | ||||
latest_length, | ||||
) | ) | |||
line += f" <fg={color}>{version:{latest_length}}</>" | ||||
if write_why: | ||||
required_by = reverse_deps(locked, locked_repo) | ||||
if required_by: | ||||
content = ",".join(required_by.keys()) | ||||
# subtract 6 for ' from ' | ||||
line += f" from {content:{required_by_length - 6}}" | ||||
else: | ||||
line += " " * required_by_length | ||||
if write_description: | if write_description: | |||
description = locked.description | description = locked.description | |||
remaining = width - name_length - version_length - 4 | remaining = ( | |||
width - name_length - version_length - required_by_length - | ||||
4 | ||||
) | ||||
if show_latest: | if show_latest: | |||
remaining -= latest_length | remaining -= latest_length | |||
if len(locked.description) > remaining: | if len(locked.description) > remaining: | |||
description = description[: remaining - 3] + "..." | description = description[: remaining - 3] + "..." | |||
line += " " + description | line += " " + description | |||
self.line(line) | self.line(line) | |||
def display_package_tree(self, io, package, installed_repo): | return 0 | |||
io.write("<c1>{}</c1>".format(package.pretty_name)) | ||||
def display_package_tree( | ||||
self, | ||||
io: IO, | ||||
package: Package, | ||||
installed_packages: list[Package], | ||||
why_package: Package | None = None, | ||||
) -> None: | ||||
io.write(f"<c1>{package.pretty_name}</c1>") | ||||
description = "" | description = "" | |||
if package.description: | if package.description: | |||
description = " " + package.description | description = " " + package.description | |||
io.write_line(" <b>{}</b>{}".format(package.pretty_version, description) | io.write_line(f" <b>{package.pretty_version}</b>{description}") | |||
) | ||||
if why_package is not None: | ||||
dependencies = [p for p in package.requires if p.name == why_package | ||||
.name] | ||||
else: | ||||
dependencies = package.requires | ||||
dependencies = sorted( | ||||
dependencies, | ||||
key=lambda x: x.name, | ||||
) | ||||
dependencies = package.requires | ||||
dependencies = sorted(dependencies, key=lambda x: x.name) | ||||
tree_bar = "├" | tree_bar = "├" | |||
j = 0 | ||||
total = len(dependencies) | total = len(dependencies) | |||
for dependency in dependencies: | for i, dependency in enumerate(dependencies, 1): | |||
j += 1 | if i == total: | |||
if j == total: | ||||
tree_bar = "└" | tree_bar = "└" | |||
level = 1 | level = 1 | |||
color = self.colors[level] | color = self.colors[level] | |||
info = "{tree_bar}── <{color}>{name}</{color}> {constraint}".format( | info = ( | |||
tree_bar=tree_bar, | f"{tree_bar}── <{color}>{dependency.name}</{color}>" | |||
color=color, | f" {dependency.pretty_constraint}" | |||
name=dependency.name, | ||||
constraint=dependency.pretty_constraint, | ||||
) | ) | |||
self._write_tree_line(io, info) | self._write_tree_line(io, info) | |||
tree_bar = tree_bar.replace("└", " ") | tree_bar = tree_bar.replace("└", " ") | |||
packages_in_tree = [package.name, dependency.name] | packages_in_tree = [package.name, dependency.name] | |||
self._display_tree( | self._display_tree( | |||
io, dependency, installed_repo, packages_in_tree, tree_bar, leve | io, | |||
l + 1 | dependency, | |||
installed_packages, | ||||
packages_in_tree, | ||||
tree_bar, | ||||
level + 1, | ||||
) | ) | |||
def _display_tree( | def _display_tree( | |||
self, | self, | |||
io, | io: IO, | |||
dependency, | dependency: Dependency, | |||
installed_repo, | installed_packages: list[Package], | |||
packages_in_tree, | packages_in_tree: list[NormalizedName], | |||
previous_tree_bar="├", | previous_tree_bar: str = "├", | |||
level=1, | level: int = 1, | |||
): | ) -> None: | |||
previous_tree_bar = previous_tree_bar.replace("├", "│") | previous_tree_bar = previous_tree_bar.replace("├", "│") | |||
dependencies = [] | dependencies = [] | |||
for package in installed_repo.packages: | for package in installed_packages: | |||
if package.name == dependency.name: | if package.name == dependency.name: | |||
dependencies = package.requires | dependencies = package.requires | |||
break | break | |||
dependencies = sorted(dependencies, key=lambda x: x.name) | dependencies = sorted( | |||
dependencies, | ||||
key=lambda x: x.name, | ||||
) | ||||
tree_bar = previous_tree_bar + " ├" | tree_bar = previous_tree_bar + " ├" | |||
i = 0 | ||||
total = len(dependencies) | total = len(dependencies) | |||
for dependency in dependencies: | for i, dependency in enumerate(dependencies, 1): | |||
i += 1 | ||||
current_tree = packages_in_tree | current_tree = packages_in_tree | |||
if i == total: | if i == total: | |||
tree_bar = previous_tree_bar + " └" | tree_bar = previous_tree_bar + " └" | |||
color_ident = level % len(self.colors) | color_ident = level % len(self.colors) | |||
color = self.colors[color_ident] | color = self.colors[color_ident] | |||
circular_warn = "" | circular_warn = "" | |||
if dependency.name in current_tree: | if dependency.name in current_tree: | |||
circular_warn = "(circular dependency aborted here)" | circular_warn = "(circular dependency aborted here)" | |||
info = "{tree_bar}── <{color}>{name}</{color}> {constraint} {warn}". | info = ( | |||
format( | f"{tree_bar}── <{color}>{dependency.name}</{color}>" | |||
tree_bar=tree_bar, | f" {dependency.pretty_constraint} {circular_warn}" | |||
color=color, | ||||
name=dependency.name, | ||||
constraint=dependency.pretty_constraint, | ||||
warn=circular_warn, | ||||
) | ) | |||
self._write_tree_line(io, info) | self._write_tree_line(io, info) | |||
tree_bar = tree_bar.replace("└", " ") | tree_bar = tree_bar.replace("└", " ") | |||
if dependency.name not in current_tree: | if dependency.name not in current_tree: | |||
current_tree.append(dependency.name) | current_tree.append(dependency.name) | |||
self._display_tree( | self._display_tree( | |||
io, dependency, installed_repo, current_tree, tree_bar, leve | io, | |||
l + 1 | dependency, | |||
installed_packages, | ||||
current_tree, | ||||
tree_bar, | ||||
level + 1, | ||||
) | ) | |||
def _write_tree_line(self, io, line): | def _write_tree_line(self, io: IO, line: str) -> None: | |||
if not io.output.supports_ansi(): | if not io.output.supports_utf8(): | |||
line = line.replace("└", "`-") | line = line.replace("└", "`-") | |||
line = line.replace("├", "|-") | line = line.replace("├", "|-") | |||
line = line.replace("──", "-") | line = line.replace("──", "-") | |||
line = line.replace("│", "|") | line = line.replace("│", "|") | |||
io.write_line(line) | io.write_line(line) | |||
def init_styles(self, io): | def init_styles(self, io: IO) -> None: | |||
from clikit.api.formatter import Style | from cleo.formatters.style import Style | |||
for color in self.colors: | for color in self.colors: | |||
style = Style(color).fg(color) | style = Style(color) | |||
io.output.formatter.add_style(style) | io.output.formatter.set_style(color, style) | |||
io.error_output.formatter.add_style(style) | io.error_output.formatter.set_style(color, style) | |||
def find_latest_package(self, package, include_dev): | def find_latest_package( | |||
from clikit.io import NullIO | self, package: Package, root: ProjectPackage | |||
) -> Package | None: | ||||
from cleo.io.null_io import NullIO | ||||
from poetry.puzzle.provider import Provider | from poetry.puzzle.provider import Provider | |||
from poetry.version.version_selector import VersionSelector | from poetry.version.version_selector import VersionSelector | |||
# find the latest version allowed in this pool | # find the latest version allowed in this pool | |||
if package.source_type in ("git", "file", "directory"): | if package.source_type in ("git", "file", "directory"): | |||
requires = self.poetry.package.requires | requires = root.all_requires | |||
if include_dev: | ||||
requires = requires + self.poetry.package.dev_requires | ||||
for dep in requires: | for dep in requires: | |||
if dep.name == package.name: | if dep.name == package.name: | |||
provider = Provider(self.poetry.package, self.poetry.pool, N | provider = Provider(root, self.poetry.pool, NullIO()) | |||
ullIO()) | return provider.search_for_direct_origin_dependency(dep) | |||
if dep.is_vcs(): | ||||
return provider.search_for_vcs(dep)[0] | ||||
if dep.is_file(): | ||||
return provider.search_for_file(dep)[0] | ||||
if dep.is_directory(): | ||||
return provider.search_for_directory(dep)[0] | ||||
name = package.name | name = package.name | |||
selector = VersionSelector(self.poetry.pool) | selector = VersionSelector(self.poetry.pool) | |||
return selector.find_best_candidate(name, ">={}".format(package.pretty_v ersion)) | return selector.find_best_candidate(name, f">={package.pretty_version}") | |||
def get_update_status(self, latest, package): | def get_update_status(self, latest: Package, package: Package) -> str: | |||
from poetry.core.semver import parse_constraint | from poetry.core.semver.helpers import parse_constraint | |||
if latest.full_pretty_version == package.full_pretty_version: | if latest.full_pretty_version == package.full_pretty_version: | |||
return "up-to-date" | return "up-to-date" | |||
constraint = parse_constraint("^" + package.pretty_version) | constraint = parse_constraint("^" + package.pretty_version) | |||
if latest.version and constraint.allows(latest.version): | if constraint.allows(latest.version): | |||
# It needs an immediate semver-compliant upgrade | # It needs an immediate semver-compliant upgrade | |||
return "semver-safe-update" | return "semver-safe-update" | |||
# it needs an upgrade but has potential BC breaks so is not urgent | # it needs an upgrade but has potential BC breaks so is not urgent | |||
return "update-possible" | return "update-possible" | |||
def get_installed_status(self, locked, installed_repo): | def get_installed_status( | |||
for package in installed_repo.packages: | self, locked: Package, installed_packages: list[Package] | |||
) -> str: | ||||
for package in installed_packages: | ||||
if locked.name == package.name: | if locked.name == package.name: | |||
return "installed" | return "installed" | |||
return "not-installed" | return "not-installed" | |||
End of changes. 64 change blocks. | ||||
133 lines changed or deleted | 259 lines changed or added |