# __
# ____ _____ | | _____
# / \\__ \ | | \__ \
# | | \/ __ \| |__/ __ \_
# |___| (____ /____(____ /
# \/ \/ \/
#
# 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 .
"""Module for Nala debfile and dependency subclasses."""
from __future__ import annotations
import contextlib
from typing import List, cast
import apt_pkg
from apt.debfile import DebPackage
from apt.package import Version, VersionList
from nala.cache import Cache
class NalaDebPackage(DebPackage):
"""A subclass for DebPackage to have attributes similar to Package."""
def __init__(self, filename: str, cache: Cache) -> None:
"""Subclass for DebPackage."""
super().__init__(filename, cache)
self.filename = filename
self._cache = cache
@property
def name(self) -> str:
"""Return the package name. This may have :arch appended."""
return self.pkgname
def _get_depends(self, _type: str) -> list[list[tuple[str, str, str]]]:
"""List of packages on which this package depends on."""
depends = []
with contextlib.suppress(KeyError):
depends.extend(apt_pkg.parse_depends(self._sections[_type], False))
return depends
def get_dependencies(self, *types: str) -> list[NalaDep]:
"""Return a list of Dependency objects for the given types.
Multiple types can be specified. Possible types are:
'Breaks', 'Conflicts', 'Depends', 'Enhances', 'PreDepends',
'Recommends', 'Replaces', 'Suggests'
Additional types might be added in the future.
"""
depends_list = []
for _type in types:
for dep in self._get_depends(_type):
base_deps = [
NalaBaseDep(*dep_or, _type, cast(Cache, self._cache))
for dep_or in dep
]
depends_list.append(NalaDep(base_deps, _type))
return depends_list
def installed_size(self) -> int:
"""Get the installed size. Returns 0 if it cannot be found."""
with contextlib.suppress(KeyError):
return int(self._sections["Installed-Size"])
return 0
@property
def dependencies(self) -> list[NalaDep]:
"""Return the dependencies of the package version."""
return self.get_dependencies("PreDepends", "Depends")
class NalaBaseDep:
"""Base Dependency class to contain debfile deps."""
def __init__( # pylint: disable=too-many-arguments
self,
name: str,
version: str,
relation: str,
rawtype: str,
cache: Cache,
) -> None:
"""Initialize Base Dependency class to contain debfile deps."""
self.name = name
self.relation = relation
self.relation_deb = relation
self.version = version
self.rawtype = rawtype
self.cache = cache
def __repr__(self) -> str:
"""Return a string representation of the instance."""
return (
f""
)
@property
def rawstr(self) -> str:
"""Return a string representation of the dependency.
Returns the string representation of the dependency as it would be
written in the debian/control file. The string representation does not
include the type of the dependency.
Example for an unversioned dependency:
python3
Example for a versioned dependency:
python3 >= 3.2
.. versionadded:: 1.0.0
"""
if self.version:
return f"{self.name} {self.relation} {self.version}"
return self.name
@property
def target_versions(self) -> VersionList:
"""Return the target versions if they exist."""
try:
pkg = self.cache[self.name]
except KeyError:
return cast(VersionList, [])
return pkg.versions
@property
def installed_target_versions(self) -> list[Version]:
"""Return the installed target versions if they exist."""
return [ver for ver in self.target_versions if ver.is_installed]
# Nuitka doesn't seem to like typing with the lower case list on this class.
class NalaDep(List[NalaBaseDep]):
"""Dependency class to contain debfile deps."""
def __init__(self, base_deps: list[NalaBaseDep], rawtype: str) -> None:
"""Dependency class to contain debfile deps."""
super().__init__(base_deps)
self.rawtype = rawtype
def __repr__(self) -> str:
"""Return a string representation of the instance."""
return f""
@property
def rawstr(self) -> str:
"""Return a string representation of the Or-group of dependencies.
Returns the string representation of the Or-group of dependencies as it
would be written in the debian/control file. The string representation
does not include the type of the Or-group of dependencies.
Example:
-------
python2 >= 2.7 | python3
"""
return " | ".join(bd.rawstr for bd in self)
@property
def target_versions(self) -> list[Version]:
"""List all Version objects which satisfy this Or-group of deps."""
tvers: set[Version] = set()
for base_dep in self:
for tver in base_dep.target_versions:
tvers.add(tver)
return list(tvers)
@property
def installed_target_versions(self) -> list[Version]:
"""List all Version installed Version objects which satisfy this dep."""
return [ver for ver in self.target_versions if ver.is_installed]