summaryrefslogtreecommitdiff
path: root/apt/debfile.py
diff options
context:
space:
mode:
Diffstat (limited to 'apt/debfile.py')
-rw-r--r--apt/debfile.py247
1 files changed, 154 insertions, 93 deletions
diff --git a/apt/debfile.py b/apt/debfile.py
index adf9b348..3f4bca4b 100644
--- a/apt/debfile.py
+++ b/apt/debfile.py
@@ -17,6 +17,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
"""Classes for working with locally available Debian packages."""
+from __future__ import print_function
+
import apt
import apt_inst
import apt_pkg
@@ -25,19 +27,21 @@ import os
import sys
from apt_pkg import gettext as _
-from StringIO import StringIO
+from io import BytesIO
+
class NoDebArchiveException(IOError):
"""Exception which is raised if a file is no Debian archive."""
+
class DebPackage(object):
"""A Debian Package (.deb file)."""
# Constants for comparing the local package file with the version
# in the cache
- (VERSION_NONE,
- VERSION_OUTDATED,
- VERSION_SAME,
+ (VERSION_NONE,
+ VERSION_OUTDATED,
+ VERSION_SAME,
VERSION_NEWER) = range(4)
debug = 0
@@ -68,7 +72,7 @@ class DebPackage(object):
self._sections = apt_pkg.TagSection(control)
self.pkgname = self._sections["Package"]
self._check_was_run = False
-
+
def __getitem__(self, key):
return self._sections[key]
@@ -91,29 +95,31 @@ class DebPackage(object):
""" return the list of files in control.tar.gt """
control = []
try:
- self._debfile.control.go(lambda item, data: control.append(item.name))
+ self._debfile.control.go(
+ lambda item, data: control.append(item.name))
except SystemError:
return [_("List of control files for '%s' could not be read") %
self.filename]
return sorted(control)
-
# helper that will return a pkgname with a multiarch suffix if needed
- def _maybe_append_multiarch_suffix(self, pkgname,
+ def _maybe_append_multiarch_suffix(self, pkgname,
in_conflict_checking=False):
# trivial cases
+ if ":" in pkgname:
+ return pkgname
if not self._multiarch:
return pkgname
elif self._cache.is_virtual_package(pkgname):
return pkgname
- elif (pkgname in self._cache and
+ elif (pkgname in self._cache and
self._cache[pkgname].candidate and
self._cache[pkgname].candidate.architecture == "all"):
return pkgname
# now do the real multiarch checking
multiarch_pkgname = "%s:%s" % (pkgname, self._multiarch)
# the upper layers will handle this
- if not multiarch_pkgname in self._cache:
+ if multiarch_pkgname not in self._cache:
return multiarch_pkgname
# now check the multiarch state
cand = self._cache[multiarch_pkgname].candidate._cand
@@ -124,8 +130,8 @@ class DebPackage(object):
return pkgname
# for conflicts we need a special case here, any not multiarch enabled
# package has a implicit conflict
- if (in_conflict_checking and
- not (cand.multi_arch & cand.MULTI_ARCH_SAME)):
+ if (in_conflict_checking and
+ not (cand.multi_arch & cand.MULTI_ARCH_SAME)):
return pkgname
return multiarch_pkgname
@@ -146,9 +152,11 @@ class DebPackage(object):
depname = self._maybe_append_multiarch_suffix(depname)
# check for virtual pkgs
- if not depname in self._cache:
+ if depname not in self._cache:
if self._cache.is_virtual_package(depname):
- self._dbg(3, "_is_or_group_satisfied(): %s is virtual dep" % depname)
+ self._dbg(
+ 3, "_is_or_group_satisfied(): %s is virtual dep" %
+ depname)
for pkg in self._cache.get_providing_packages(depname):
if pkg.is_installed:
return True
@@ -161,13 +169,15 @@ class DebPackage(object):
# if no real dependency is installed, check if there is
# a package installed that provides this dependency
# (e.g. scrollkeeper dependecies are provided by rarian-compat)
- # but only do that if there is no version required in the
+ # but only do that if there is no version required in the
# dependency (we do not supprot versionized dependencies)
if not oper:
for ppkg in self._cache.get_providing_packages(
depname, include_nonvirtual=True):
if ppkg.is_installed:
- self._dbg(3, "found installed '%s' that provides '%s'" % (ppkg.name, depname))
+ self._dbg(
+ 3, "found installed '%s' that provides '%s'" % (
+ ppkg.name, depname))
return True
return False
@@ -180,7 +190,7 @@ class DebPackage(object):
depname = self._maybe_append_multiarch_suffix(depname)
# if we don't have it in the cache, it may be virtual
- if not depname in self._cache:
+ if depname not in self._cache:
if not self._cache.is_virtual_package(depname):
continue
providers = self._cache.get_providing_packages(depname)
@@ -210,16 +220,19 @@ class DebPackage(object):
or_str += dep[0]
if ver and oper:
or_str += " (%s %s)" % (dep[2], dep[1])
- if dep != or_group[len(or_group)-1]:
+ if dep != or_group[len(or_group) - 1]:
or_str += "|"
- self._failure_string += _("Dependency is not satisfiable: %s\n") % or_str
+ self._failure_string += _(
+ "Dependency is not satisfiable: %s\n") % or_str
return False
def _check_single_pkg_conflict(self, pkgname, ver, oper):
"""Return True if a pkg conflicts with a real installed/marked pkg."""
# FIXME: deal with conflicts against its own provides
# (e.g. Provides: ftp-server, Conflicts: ftp-server)
- self._dbg(3, "_check_single_pkg_conflict() pkg='%s' ver='%s' oper='%s'" % (pkgname, ver, oper))
+ self._dbg(
+ 3, "_check_single_pkg_conflict() pkg='%s' ver='%s' oper='%s'" % (
+ pkgname, ver, oper))
pkg = self._cache[pkgname]
if pkg.is_installed:
pkgver = pkg.installed.version
@@ -232,7 +245,7 @@ class DebPackage(object):
#print "pkgver: %s " % pkgver
#print "oper: %s " % oper
if (apt_pkg.check_dep(pkgver, oper, ver) and not
- self.replaces_real_pkg(pkgname, oper, ver)):
+ self.replaces_real_pkg(pkgname, oper, ver)):
self._failure_string += _("Conflicts with the installed package "
"'%s'") % pkg.name
self._dbg(3, "conflicts with installed pkg '%s'" % pkg.name)
@@ -253,7 +266,7 @@ class DebPackage(object):
depname, in_conflict_checking=True)
# check conflicts with virtual pkgs
- if not depname in self._cache:
+ if depname not in self._cache:
# FIXME: we have to check for virtual replaces here as
# well (to pass tests/gdebi-test8.deb)
if self._cache.is_virtual_package(depname):
@@ -263,8 +276,8 @@ class DebPackage(object):
if self.pkgname == pkg.name:
self._dbg(3, "conflict on self, ignoring")
continue
- if self._check_single_pkg_conflict(pkg.name, ver,
- oper):
+ if self._check_single_pkg_conflict(
+ pkg.name, ver, oper):
self._installed_conflicts.add(pkg.name)
continue
if self._check_single_pkg_conflict(depname, ver, oper):
@@ -276,7 +289,7 @@ class DebPackage(object):
"""List of package names conflicting with this package."""
key = "Conflicts"
try:
- return apt_pkg.parse_depends(self._sections[key])
+ return apt_pkg.parse_depends(self._sections[key], False)
except KeyError:
return []
@@ -287,7 +300,8 @@ class DebPackage(object):
# find depends
for key in "Depends", "Pre-Depends":
try:
- depends.extend(apt_pkg.parse_depends(self._sections[key]))
+ depends.extend(
+ apt_pkg.parse_depends(self._sections[key], False))
except KeyError:
pass
return depends
@@ -297,7 +311,7 @@ class DebPackage(object):
"""List of virtual packages which are provided by this package."""
key = "Provides"
try:
- return apt_pkg.parse_depends(self._sections[key])
+ return apt_pkg.parse_depends(self._sections[key], False)
except KeyError:
return []
@@ -306,7 +320,7 @@ class DebPackage(object):
"""List of packages which are replaced by this package."""
key = "Replaces"
try:
- return apt_pkg.parse_depends(self._sections[key])
+ return apt_pkg.parse_depends(self._sections[key], False)
except KeyError:
return []
@@ -347,22 +361,22 @@ class DebPackage(object):
return res
def check_breaks_existing_packages(self):
- """
- check if installing the package would break exsisting
+ """
+ check if installing the package would break exsisting
package on the system, e.g. system has:
smc depends on smc-data (= 1.4)
and user tries to installs smc-data 1.6
"""
# show progress information as this step may take some time
size = float(len(self._cache))
- steps = max(int(size/50), 1)
+ steps = max(int(size / 50), 1)
debver = self._sections["Version"]
debarch = self._sections["Architecture"]
# store what we provide so that we can later check against that
- provides = [ x[0][0] for x in self.provides]
+ provides = [x[0][0] for x in self.provides]
for (i, pkg) in enumerate(self._cache):
- if i%steps == 0:
- self._cache.op_progress.update(float(i)/size*100.0)
+ if i % steps == 0:
+ self._cache.op_progress.update(float(i) / size * 100.0)
if not pkg.is_installed:
continue
# check if the exising dependencies are still satisfied
@@ -371,14 +385,21 @@ class DebPackage(object):
for dep_or in pkg.installed.dependencies:
for dep in dep_or.or_dependencies:
if dep.name == self.pkgname:
- if not apt_pkg.check_dep(debver, dep.relation, dep.version):
+ if not apt_pkg.check_dep(
+ debver, dep.relation, dep.version):
self._dbg(2, "would break (depends) %s" % pkg.name)
- # TRANSLATORS: the first '%s' is the package that breaks, the second the dependency that makes it break, the third the relation (e.g. >=) and the latest the version for the releation
- self._failure_string += _("Breaks existing package '%(pkgname)s' dependency %(depname)s (%(deprelation)s %(depversion)s)") % {
- 'pkgname' : pkg.name,
- 'depname' : dep.name,
- 'deprelation' : dep.relation,
- 'depversion' : dep.version}
+ # TRANSLATORS: the first '%s' is the package that
+ # breaks, the second the dependency that makes it
+ # break, the third the relation (e.g. >=) and the
+ # latest the version for the releation
+ self._failure_string += _(
+ "Breaks existing package '%(pkgname)s' "
+ "dependency %(depname)s "
+ "(%(deprelation)s %(depversion)s)") % {
+ 'pkgname': pkg.name,
+ 'depname': dep.name,
+ 'deprelation': dep.relation,
+ 'depversion': dep.version}
self._cache.op_progress.done()
return False
# now check if there are conflicts against this package on
@@ -386,25 +407,41 @@ class DebPackage(object):
if "Conflicts" in ver.depends_list:
for conflicts_ver_list in ver.depends_list["Conflicts"]:
for c_or in conflicts_ver_list:
- if c_or.target_pkg.name == self.pkgname and c_or.target_pkg.architecture == debarch:
- if apt_pkg.check_dep(debver, c_or.comp_type, c_or.target_ver):
- self._dbg(2, "would break (conflicts) %s" % pkg.name)
- # TRANSLATORS: the first '%s' is the package that conflicts, the second the packagename that it conflicts with (so the name of the deb the user tries to install), the third is the relation (e.g. >=) and the last is the version for the relation
- self._failure_string += _("Breaks existing package '%(pkgname)s' conflict: %(targetpkg)s (%(comptype)s %(targetver)s)") % {
- 'pkgname' : pkg.name,
- 'targetpkg' : c_or.target_pkg.name,
- 'comptype' : c_or.comp_type,
- 'targetver' : c_or.target_ver }
+ if (c_or.target_pkg.name == self.pkgname and
+ c_or.target_pkg.architecture == debarch):
+ if apt_pkg.check_dep(
+ debver, c_or.comp_type, c_or.target_ver):
+ self._dbg(
+ 2, "would break (conflicts) %s" % pkg.name)
+ # TRANSLATORS: the first '%s' is the package
+ # that conflicts, the second the packagename
+ # that it conflicts with (so the name of the
+ # deb the user tries to install), the third is
+ # the relation (e.g. >=) and the last is the
+ # version for the relation
+ self._failure_string += _(
+ "Breaks existing package '%(pkgname)s' "
+ "conflict: %(targetpkg)s "
+ "(%(comptype)s %(targetver)s)") % {
+ 'pkgname': pkg.name,
+ 'targetpkg': c_or.target_pkg.name,
+ 'comptype': c_or.comp_type,
+ 'targetver': c_or.target_ver}
self._cache.op_progress.done()
return False
if (c_or.target_pkg.name in provides and
- self.pkgname != pkg.name):
- self._dbg(2, "would break (conflicts) %s" % provides)
- self._failure_string += _("Breaks existing package '%(pkgname)s' that conflict: '%(targetpkg)s'. But the '%(debfile)s' provides it via: '%(provides)s'") % {
- 'provides' : ",".join(provides),
- 'debfile' : self.filename,
- 'targetpkg' : c_or.target_pkg.name,
- 'pkgname' : pkg.name }
+ self.pkgname != pkg.name):
+ self._dbg(
+ 2, "would break (conflicts) %s" % provides)
+ self._failure_string += _(
+ "Breaks existing package '%(pkgname)s' "
+ "that conflict: '%(targetpkg)s'. But the "
+ "'%(debfile)s' provides it via: "
+ "'%(provides)s'") % {
+ 'provides': ",".join(provides),
+ 'debfile': self.filename,
+ 'targetpkg': c_or.target_pkg.name,
+ 'pkgname': pkg.name}
self._cache.op_progress.done()
return False
self._cache.op_progress.done()
@@ -419,6 +456,11 @@ class DebPackage(object):
"""
self._dbg(3, "compare_to_version_in_cache")
pkgname = self._sections["Package"]
+ architecture = self._sections["Architecture"]
+
+ # Arch qualify the package name
+ pkgname = ":".join([pkgname, architecture])
+
debver = self._sections["Version"]
self._dbg(1, "debver: %s" % debver)
if pkgname in self._cache:
@@ -439,19 +481,19 @@ class DebPackage(object):
return self.VERSION_OUTDATED
return self.VERSION_NONE
- def check(self):
+ def check(self, allow_downgrade=False):
"""Check if the package is installable."""
self._dbg(3, "check")
self._check_was_run = True
# check arch
- if not "Architecture" in self._sections:
+ if "Architecture" not in self._sections:
self._dbg(1, "ERROR: no architecture field")
self._failure_string = _("No Architecture field in the package")
return False
arch = self._sections["Architecture"]
- if arch != "all" and arch != apt_pkg.config.find("APT::Architecture"):
+ if arch != "all" and arch != apt_pkg.config.find("APT::Architecture"):
if arch in apt_pkg.get_architectures():
self._multiarch = arch
self.pkgname = "%s:%s" % (self.pkgname, self._multiarch)
@@ -462,10 +504,12 @@ class DebPackage(object):
return False
# check version
- if self.compare_to_version_in_cache() == self.VERSION_OUTDATED:
+ if (not allow_downgrade and
+ self.compare_to_version_in_cache() == self.VERSION_OUTDATED):
if self._cache[self.pkgname].installed:
# the deb is older than the installed
- self._failure_string = _("A later version is already installed")
+ self._failure_string = _(
+ "A later version is already installed")
return False
# FIXME: this sort of error handling sux
@@ -475,7 +519,7 @@ class DebPackage(object):
if not self.check_conflicts():
return False
- # check if installing it would break anything on the
+ # check if installing it would break anything on the
# current system
if not self.check_breaks_existing_packages():
return False
@@ -499,19 +543,18 @@ class DebPackage(object):
def satisfy_depends_str(self, dependsstr):
"""Satisfy the dependencies in the given string."""
- return self._satisfy_depends(apt_pkg.parse_depends(dependsstr))
+ return self._satisfy_depends(apt_pkg.parse_depends(dependsstr, False))
def _satisfy_depends(self, depends):
"""Satisfy the dependencies."""
# turn off MarkAndSweep via a action group (if available)
try:
_actiongroup = apt_pkg.ActionGroup(self._cache._depcache)
+ _actiongroup # pyflakes
except AttributeError:
pass
# check depends
for or_group in depends:
- #print "or_group: %s" % or_group
- #print "or_group satified: %s" % self._is_or_group_satisfied(or_group)
if not self._is_or_group_satisfied(or_group):
if not self._satisfy_or_group(or_group):
return False
@@ -530,7 +573,8 @@ class DebPackage(object):
"""Return missing dependencies."""
self._dbg(1, "Installing: %s" % self._need_pkgs)
if not self._check_was_run:
- raise AttributeError("property only available after check() was run")
+ raise AttributeError(
+ "property only available after check() was run")
return self._need_pkgs
@property
@@ -543,7 +587,8 @@ class DebPackage(object):
remove = []
unauthenticated = []
if not self._check_was_run:
- raise AttributeError("property only available after check() was run")
+ raise AttributeError(
+ "property only available after check() was run")
for pkg in self._cache:
if pkg.marked_install or pkg.marked_upgrade:
install.append(pkg.name)
@@ -562,7 +607,7 @@ class DebPackage(object):
def to_hex(in_data):
hex = ""
for (i, c) in enumerate(in_data):
- if i%80 == 0:
+ if i % 80 == 0:
hex += "\n"
hex += "%2.2x " % ord(c)
return hex
@@ -585,20 +630,20 @@ class DebPackage(object):
else:
s += chr(b)
return s
-
+
def _get_content(self, part, name, auto_decompress=True, auto_hex=True):
if name.startswith("./"):
name = name[2:]
data = part.extractdata(name)
# check for zip content
if name.endswith(".gz") and auto_decompress:
- io = StringIO(data)
+ io = BytesIO(data)
gz = gzip.GzipFile(fileobj=io)
- data = _("Automatically decompressed:\n\n")
+ data = _("Automatically decompressed:\n\n").encode("utf-8")
data += gz.read()
# auto-convert to hex
try:
- data = unicode(data, "utf-8")
+ data = data.decode("utf-8")
except Exception:
new_data = _("Automatically converted to printable ascii:\n")
new_data += self.to_strish(data)
@@ -622,7 +667,7 @@ class DebPackage(object):
def _dbg(self, level, msg):
"""Write debugging output to sys.stderr."""
if level <= self.debug:
- print >> sys.stderr, msg
+ print(msg, file=sys.stderr)
def install(self, install_progress=None):
"""Install the package."""
@@ -640,6 +685,7 @@ class DebPackage(object):
install_progress.finishUpdate()
return res
+
class DscSrcPackage(DebPackage):
"""A locally available source package."""
@@ -664,28 +710,42 @@ class DscSrcPackage(DebPackage):
"""Return the dependencies of the package"""
return self._conflicts
+ @property
+ def filelist(self):
+ """Return the list of files associated with this dsc file"""
+ # Files stanza looks like (hash, size, filename, ...)
+ return self._sections['Files'].split()[2::3]
+
def open(self, file):
"""Open the package."""
depends_tags = ["Build-Depends", "Build-Depends-Indep"]
conflicts_tags = ["Build-Conflicts", "Build-Conflicts-Indep"]
- fobj = open(file)
+ fd = apt_pkg.open_maybe_clear_signed_file(file)
+ fobj = os.fdopen(fd)
tagfile = apt_pkg.TagFile(fobj)
try:
for sec in tagfile:
+ # we only care about the stanza with the "Format:" tag, the
+ # rest is gpg signature noise. we should probably have
+ # bindings for apts OpenMaybeClearsignedFile()
+ if "Format" not in sec:
+ continue
for tag in depends_tags:
- if not tag in sec:
+ if tag not in sec:
continue
self._depends.extend(apt_pkg.parse_src_depends(sec[tag]))
for tag in conflicts_tags:
- if not tag in sec:
+ if tag not in sec:
continue
self._conflicts.extend(apt_pkg.parse_src_depends(sec[tag]))
if 'Source' in sec:
self.pkgname = sec['Source']
if 'Binary' in sec:
- self.binaries = sec['Binary'].split(', ')
- if 'Version' in sec:
- self._sections['Version'] = sec['Version']
+ self.binaries = [b.strip() for b in
+ sec['Binary'].split(',')]
+ for tag in sec.keys():
+ if tag in sec:
+ self._sections[tag] = sec[tag]
finally:
del tagfile
fobj.close()
@@ -695,7 +755,7 @@ class DscSrcPackage(DebPackage):
" ".join(self.binaries))
self._sections["Description"] = s
self._check_was_run = False
-
+
def check(self):
"""Check if the package is installable.."""
if not self.check_conflicts():
@@ -709,6 +769,7 @@ class DscSrcPackage(DebPackage):
# after _satisfy_depends() should probably be done
return self._satisfy_depends(self.depends)
+
def _test():
"""Test function"""
from apt.cache import Cache
@@ -717,25 +778,25 @@ def _test():
cache = Cache()
vp = "www-browser"
- print "%s virtual: %s" % (vp, cache.is_virtual_package(vp))
+ print("%s virtual: %s" % (vp, cache.is_virtual_package(vp)))
providers = cache.get_providing_packages(vp)
- print "Providers for %s :" % vp
+ print("Providers for %s :" % vp)
for pkg in providers:
- print " %s" % pkg.name
+ print(" %s" % pkg.name)
d = DebPackage(sys.argv[1], cache)
- print "Deb: %s" % d.pkgname
+ print("Deb: %s" % d.pkgname)
if not d.check():
- print "can't be satified"
- print d._failure_string
- print "missing deps: %s" % d.missing_deps
- print d.required_changes
+ print("can't be satified")
+ print(d._failure_string)
+ print("missing deps: %s" % d.missing_deps)
+ print(d.required_changes)
- print d.filelist
+ print(d.filelist)
- print "Installing ..."
+ print("Installing ...")
ret = d.install(InstallProgress())
- print ret
+ print(ret)
#s = DscSrcPackage(cache, "../tests/3ddesktop_0.2.9-6.dsc")
#s.check_dep()
@@ -744,7 +805,7 @@ def _test():
s = DscSrcPackage(cache=cache)
d = "libc6 (>= 2.3.2), libaio (>= 0.3.96) | libaio1 (>= 0.3.96)"
- print s._satisfy_depends(apt_pkg.parse_depends(d))
+ print(s._satisfy_depends(apt_pkg.parse_depends(d, False)))
if __name__ == "__main__":
_test()