%PDF- %PDF-
| Direktori : /proc/thread-self/root/usr/lib/python3/dist-packages/nala/ |
| Current File : //proc/thread-self/root/usr/lib/python3/dist-packages/nala/show.py |
# __
# ____ _____ | | _____
# / \\__ \ | | \__ \
# | | \/ __ \| |__/ __ \_
# |___| (____ /____(____ /
# \/ \/ \/
#
# Copyright (C) 2021, 2022 Blake Lee
#
# This file is part of nala
#
# nala is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# nala is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with nala. If not, see <https://www.gnu.org/licenses/>.
"""Functions related to the `show` command."""
from __future__ import annotations
import re
from pathlib import Path
from random import shuffle
from typing import cast
from apt.package import BaseDependency, Dependency, Package, Version
from nala import _, color
from nala.cache import Cache
from nala.constants import ERROR_PREFIX, NOTICE_PREFIX, PACSTALL_METADATA
from nala.debfile import NalaBaseDep
from nala.options import arguments
from nala.rich import ascii_replace
from nala.utils import term, unit_str
# NOTE: This is for `nala show`
# NOTE: header info looks like "Package: steam:i386"
SHOW_INFO = _("{header} {info}") + "\n"
URL_PATTERN = re.compile(r"(https?://.*?/.*?/)")
def show_main(num: int, pkg: Package) -> int:
"""Orchestrate show command with support for all_versions."""
if num:
print(f"\n{'='*term.columns}\n")
count = len(pkg.versions)
versions = pkg.versions if arguments.all_versions else [pkg.candidate]
for ver_num, ver in enumerate(versions):
if ver is None:
print(
_("{pkg_name} has no candidate").format(
pkg_name=color(pkg.name, "YELLOW")
)
)
continue
if ver_num and not num:
print(f"\n{'='*term.columns}\n")
count -= 1
show_pkg(ver)
return count
def show_pkg(candidate: Version) -> None:
"""Start show functions."""
pkg = candidate.package
msg = f"{show_format(pkg, candidate)}\n{show_related(candidate)}"
if candidate.homepage:
msg += SHOW_INFO.format(header=color(_("Homepage:")), info=candidate.homepage)
if candidate.size:
msg += SHOW_INFO.format(
header=color(_("Download-Size:")), info=unit_str(candidate.size)
)
msg += SHOW_INFO.format(
header=color(_("APT-Sources:")), info=format_sources(candidate, pkg)
)
if candidate._translated_records:
msg += SHOW_INFO.format(
header=color(_("Description:")),
info=ascii_replace(candidate._translated_records.long_desc),
)
print(msg.strip())
def show_related(candidate: Version) -> str:
"""Show relational packages."""
msg = ""
if candidate.provides:
msg += SHOW_INFO.format(
header=color(_("Provides:")),
info=show_dep([color(name, "GREEN") for name in candidate.provides]),
)
if candidate.enhances:
msg += SHOW_INFO.format(
header=color(_("Enhances:")),
info=show_dep([color(pkg[0].name, "GREEN") for pkg in candidate.enhances]),
)
if candidate.dependencies:
depends, pre_depends = split_deps(candidate.dependencies)
if pre_depends:
msg += SHOW_INFO.format(
header=color(_("Pre-Depends:")), info=show_dep(pre_depends)
)
if depends:
msg += SHOW_INFO.format(header=color(_("Depends:")), info=show_dep(depends))
if candidate.recommends:
msg += SHOW_INFO.format(
header=color(_("Recommends:")), info=show_dep(candidate.recommends)
)
if candidate.suggests:
msg += SHOW_INFO.format(
header=color(_("Suggests:")), info=show_dep(candidate.suggests)
)
return msg + additional_related(candidate)
def additional_related(candidate: Version) -> str:
"""Show breaks, conflicts, replaces."""
msg = ""
if replaces := candidate.get_dependencies("Replaces"):
msg += SHOW_INFO.format(
header=color(_("Replaces:")),
info=show_dep([color(pkg[0].name, "GREEN") for pkg in replaces]),
)
if conflicts := candidate.get_dependencies("Conflicts"):
msg += SHOW_INFO.format(header=color(_("Conflicts:")), info=show_dep(conflicts))
if breaks := candidate.get_dependencies("Breaks"):
msg += SHOW_INFO.format(header=color(_("Breaks:")), info=show_dep(breaks))
return msg
def show_format(pkg: Package, candidate: Version) -> str:
"""Format main section for show command."""
installed = _("yes") if pkg.is_installed else _("no")
essential = _("yes") if pkg.essential else _("no")
maintainer = format_maintainer(f"{candidate.record.get('Maintainer')}".split())
show = (
SHOW_INFO.format(header=color(_("Package:")), info=color(pkg.name, "GREEN"))
+ SHOW_INFO.format(
header=color(_("Version:")), info=color(candidate.version, "BLUE")
)
+ SHOW_INFO.format(
header=color(_("Architecture:")), info=candidate.architecture
)
+ SHOW_INFO.format(header=color(_("Installed:")), info=installed)
+ SHOW_INFO.format(header=color(_("Priority:")), info=candidate.priority)
+ SHOW_INFO.format(header=color(_("Essential:")), info=essential)
+ SHOW_INFO.format(header=color(_("Section:")), info=candidate.section)
+ SHOW_INFO.format(header=color(_("Source:")), info=candidate.source_name)
)
if origin := candidate.origins[0].origin:
show += SHOW_INFO.format(header=color(_("Origin:")), info=origin)
show += SHOW_INFO.format(header=color(_("Maintainer:")), info=maintainer)
if original_maintainer := candidate.record.get("Original-Maintainer"):
show += SHOW_INFO.format(
header=color(_("Original-Maintainer:")),
info=format_maintainer(f"{original_maintainer}".split()),
)
if bugs := candidate.record.get("Bugs"):
show += SHOW_INFO.format(header=color(_("Bugs:")), info=bugs)
if installed_size := candidate.installed_size:
show += SHOW_INFO.format(
header=color(_("Installed-Size:")), info=unit_str(installed_size)
)
return show.strip()
def format_maintainer(maintainer: list[str]) -> str:
"""Format email in maintainer line."""
maint_list = []
for line in maintainer:
if ">" in line:
line = color(line[1:-1], "BLUE")
line = color("<") + line + color(">")
maint_list.append(line)
return " ".join(maint_list)
def show_dep(dependency: list[Dependency] | list[str]) -> str:
"""Print dependencies for show."""
if isinstance(dependency[0], str):
dependency.sort()
if len(dependency) > 4:
return "\n " + "\n ".join(cast(list[str], dependency))
return ", ".join(cast(list[str], dependency))
dependency = dedupe_deps(cast(list[Dependency], dependency))
join_list = []
msg = ""
same_line = True
if len(dependency) > 4:
same_line = False
msg += "\n"
for dep_list in dependency:
dep_print = ""
for num, dep in enumerate(dep_list):
assert isinstance(dep, BaseDependency)
if num == 0:
dep_print = format_dep(dep, num)
else:
dep_print += format_dep(dep, num)
if same_line:
join_list.append(dep_print.strip())
continue
msg += f"{dep_print}\n"
if same_line:
return ", ".join(join_list)
return msg.rstrip()
def format_dep(dep: BaseDependency | NalaBaseDep, iteration: int = 0) -> str:
"""Format dependencies for show."""
open_paren = color("(")
close_paren = color(")")
name = color(dep.name, "GREEN")
if dep.rawtype in ("Breaks", "Conflicts"):
name = color(dep.name, "RED")
relation = color(dep.relation)
version = color(dep.version, "BLUE")
indent = color(" | ") if iteration > 0 else " "
final = f"{name} {open_paren}{relation} {version}{close_paren}"
return indent + final if dep.relation else indent + name
def format_sources(candidate: Version, pkg: Package) -> str:
"""Show apt sources."""
origin = candidate.origins[0]
if origin.archive == "now":
return f"{get_local_source(pkg.shortname)}"
return (
f"{source_url(candidate.uris)} {origin.archive}/"
f"{origin.component} {candidate.architecture} Packages"
)
def source_url(uris: list[str]) -> str:
"""Return the source url."""
shuffle(uris)
for mirror in uris:
if regex := re.search(URL_PATTERN, mirror):
return regex.group()
return ""
def get_local_source(pkg_name: str) -> str:
"""Determine the local source and return it."""
postfixes = ("", "-deb", "-git", "-bin", "-app")
for postfix in postfixes:
metadata = PACSTALL_METADATA / (pkg_name + postfix)
if metadata.exists():
return parse_pacstall(metadata)
return _("local install")
def parse_pacstall(pacdata: Path) -> str:
"""Parse pacstall metadata file."""
remote = "_remoterepo="
# _remoterepo="https://github.com/pacstall/pacstall-programs"
for line in pacdata.read_text().splitlines():
if line.startswith(remote):
index = line.index("=") + 1
return color(line[index:].strip('"'), "BLUE")
return color("https://github.com/pacstall/pacstall-programs", "BLUE")
def split_deps(
depend_list: list[Dependency],
) -> tuple[list[Dependency], list[Dependency]]:
"""Split dependencies into pre-depends and depends."""
depends: list[Dependency] = []
pre_depends: list[Dependency] = []
for depend in depend_list:
if depend[0].pre_depend:
pre_depends.append(depend)
continue
depends.append(depend)
return depends, pre_depends
def dedupe_deps(duplicates: list[Dependency]) -> list[Dependency]:
"""Remove duplicate entries from a list while maintaining the order."""
deduped = []
dep_names = []
for dep in duplicates:
if dep.rawstr not in dep_names:
dep_names.append(dep.rawstr)
deduped.append(dep)
return deduped
def additional_notice(additional_records: int) -> None:
"""Print notice of additional records."""
print(
_(
"{notice} There are {num} additional records. Please use the {switch} switch to see them."
).format(
notice=NOTICE_PREFIX,
num=color(additional_records, "YELLOW"),
switch=color("'-a'", "YELLOW"),
)
)
def pkg_not_found(pkg_name: str, cache: Cache, not_found: list[str]) -> None:
"""Add not found errors to the list."""
if cache.is_virtual_package(pkg_name):
return
if cache.is_secret_virtual(pkg_name):
not_found.append(
_("{error} {package} has no version to show").format(
error=ERROR_PREFIX, package=pkg_name
)
)
return
not_found.append(
_("{error} {package} not found").format(error=ERROR_PREFIX, package=pkg_name)
)