"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "poetry/console/commands/show.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.

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

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