summaryrefslogtreecommitdiff
path: root/apt
diff options
context:
space:
mode:
authorMichael Vogt <michael.vogt@ubuntu.com>2010-07-02 14:50:52 +0200
committerMichael Vogt <michael.vogt@ubuntu.com>2010-07-02 14:50:52 +0200
commit03ce5718b828ade3940a8dd6c57a639f0d853cfc (patch)
tree7ca419cb5d6eee52c3302757ee8d40fb40a3b1cb /apt
parenta90b26aad2824d4bd224586cdf14c0ad21059ac3 (diff)
parent0c4bbfa36f36d42cc3d700e58d531e05ba898e0e (diff)
downloadpython-apt-03ce5718b828ade3940a8dd6c57a639f0d853cfc.tar.gz
merged lp:~kiwinote/python-apt/merge-gdebi-changes, this port the
DebPackage class fixes from gdebi into python-apt so that gdebi can use the class from python-apt directly
Diffstat (limited to 'apt')
-rw-r--r--apt/cache.py24
-rw-r--r--apt/debfile.py257
-rw-r--r--apt/utils.py3
3 files changed, 223 insertions, 61 deletions
diff --git a/apt/cache.py b/apt/cache.py
index 3962bb4f..26532c76 100644
--- a/apt/cache.py
+++ b/apt/cache.py
@@ -120,6 +120,7 @@ class Cache(object):
"""
if progress is None:
progress = apt.progress.base.OpProgress()
+ self.op_progress = progress
self._run_callbacks("cache_pre_open")
self._cache = apt_pkg.Cache(progress)
@@ -288,21 +289,28 @@ class Cache(object):
else:
return bool(pkg.has_provides and not pkg.has_versions)
- def get_providing_packages(self, virtual, candidate_only=True):
- """Return a list of all packages providing a virtual package.
+ def get_providing_packages(self, pkgname, candidate_only=True,
+ include_nonvirtual=False):
+ """Return a list of all packages providing a package.
Return a list of packages which provide the virtual package of the
- specified name. If 'candidate_only' is False, return all packages
- with at least one version providing the virtual package. Otherwise,
- return only those packages where the candidate version provides
- the virtual package.
+ specified name.
+
+ If 'candidate_only' is False, return all packages with at
+ least one version providing the virtual package. Otherwise,
+ return only those packages where the candidate version
+ provides the virtual package.
+
+ If 'include_nonvirtual' is True then it will search for all
+ packages providing pkgname, even if pkgname is not itself
+ a virtual pkg.
"""
providers = set()
get_candidate_ver = self._depcache.get_candidate_ver
try:
- vp = self._cache[virtual]
- if vp.has_versions:
+ vp = self._cache[pkgname]
+ if vp.has_versions and not include_nonvirtual:
return list(providers)
except KeyError:
return list(providers)
diff --git a/apt/debfile.py b/apt/debfile.py
index ccaa25e4..83a5d68c 100644
--- a/apt/debfile.py
+++ b/apt/debfile.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2005-2009 Canonical
+# Copyright (c) 2005-2010 Canonical
#
# Author: Michael Vogt <michael.vogt@ubuntu.com>
#
@@ -17,25 +17,28 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
"""Classes for working with locally available Debian packages."""
+import apt_inst
+import apt_pkg
+import gzip
import os
import sys
-import apt_inst
-import apt_pkg
from apt_pkg import gettext as _
-
-
-# Constants for comparing the local package file with the version in the cache
-(VERSION_NONE, VERSION_OUTDATED, VERSION_SAME, VERSION_NEWER) = range(4)
-
+from StringIO import StringIO
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_NEWER) = range(4)
+
_supported_data_members = ("data.tar.gz", "data.tar.bz2", "data.tar.lzma")
debug = 0
@@ -53,7 +56,7 @@ class DebPackage(object):
self.open(filename)
def open(self, filename):
- " open given debfile "
+ """ open given debfile """
self.filename = filename
self._debfile = apt_inst.DebFile(self.filename)
control = self._debfile.control.extractdata("control")
@@ -90,20 +93,35 @@ class DebPackage(object):
# check for virtual pkgs
if not depname in self._cache:
if self._cache.is_virtual_package(depname):
- self._dbg(3, "_isOrGroupSatisfied(): %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
continue
-
+ # check real dependency
inst = self._cache[depname].installed
if inst is not None and apt_pkg.check_dep(inst.version, oper, ver):
return True
+
+ # 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
+ # 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))
+ return True
return False
def _satisfy_or_group(self, or_group):
"""Try to satisfy the or_group."""
+
+ or_found = False
+ virtual_pkg = None
+
for dep in or_group:
depname, ver, oper = dep
@@ -136,19 +154,18 @@ class DebPackage(object):
or_str = ""
for dep in or_group:
or_str += dep[0]
- if dep != or_group[-1]:
+ if ver and oper:
+ or_str += " (%s %s)" % (dep[2], dep[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, "_checkSinglePkgConflict() 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
@@ -163,7 +180,7 @@ class DebPackage(object):
if (apt_pkg.check_dep(pkgver, oper, ver) and not
self.replaces_real_pkg(pkgname, oper, ver)):
self._failure_string += _("Conflicts with the installed package "
- "'%s'" % pkg.name)
+ "'%s'") % pkg.name
return True
return False
@@ -171,6 +188,9 @@ class DebPackage(object):
"""Check the or-group for conflicts with installed pkgs."""
self._dbg(2, "_check_conflicts_or_group(): %s " % (or_group))
+ or_found = False
+ virtual_pkg = None
+
for dep in or_group:
depname = dep[0]
ver = dep[1]
@@ -187,7 +207,7 @@ 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,
+ if self._check_single_pkg_conflict(pkg.name, ver,
oper):
self._installed_conflicts.add(pkg.name)
continue
@@ -209,7 +229,7 @@ class DebPackage(object):
"""List of package names on which this package depends on."""
depends = []
# find depends
- for key in "Depends", "PreDepends":
+ for key in "Depends", "Pre-Depends":
try:
depends.extend(apt_pkg.parse_depends(self._sections[key]))
except KeyError:
@@ -240,7 +260,7 @@ class DebPackage(object):
Return True if the deb packages replaces a real (not virtual)
packages named (pkgname, oper, ver).
"""
- self._dbg(3, "replacesPkg() %s %s %s" % (pkgname, oper, ver))
+ self._dbg(3, "replaces_real_pkg() %s %s %s" % (pkgname, oper, ver))
pkg = self._cache[pkgname]
if pkg.is_installed:
pkgver = pkg.installed.version
@@ -270,6 +290,57 @@ class DebPackage(object):
res = False
return res
+ def check_breaks_existing_packages(self):
+ """
+ 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 = int(size/50)
+ debver = self._sections["Version"]
+ for (i, pkg) in enumerate(self._cache):
+ 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
+ # with the package
+ ver = pkg._pkg.current_ver
+ 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):
+ 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}
+ self._cache.op_progress.done()
+ return False
+ # now check if there are conflicts against this package on
+ # the existing system
+ 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:
+ 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._failureString += _("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
+ self._cache.op_progress.done()
+ return True
+
def compare_to_version_in_cache(self, use_installed=True):
"""Compare the package to the version available in the cache.
@@ -277,42 +348,49 @@ class DebPackage(object):
and if so in what version, returns one of (VERSION_NONE,
VERSION_OUTDATED, VERSION_SAME, VERSION_NEWER).
"""
- self._dbg(3, "compareToVersionInCache")
+ self._dbg(3, "compare_to_version_in_cache")
pkgname = self._sections["Package"]
debver = self._sections["Version"]
self._dbg(1, "debver: %s" % debver)
if pkgname in self._cache:
if use_installed and self._cache[pkgname].installed:
cachever = self._cache[pkgname].installed.version
- else:
+ elif self._cache[pkgname].candidate:
cachever = self._cache[pkgname].candidate.version
+ else:
+ return self.VERSION_NONE
if cachever is not None:
- cmpres = apt_pkg.version_compare(cachever, debver)
- self._dbg(1, "CompareVersion(debver,instver): %s" % cmpres)
- if cmpres == 0:
- return VERSION_SAME
- elif cmpres < 0:
- return VERSION_NEWER
- elif cmpres > 0:
- return VERSION_OUTDATED
- return VERSION_NONE
+ cmp = apt_pkg.version_compare(cachever, debver)
+ self._dbg(1, "CompareVersion(debver,instver): %s" % cmp)
+ if cmp == 0:
+ return self.VERSION_SAME
+ elif cmp < 0:
+ return self.VERSION_NEWER
+ elif cmp > 0:
+ return self.VERSION_OUTDATED
+ return self.VERSION_NONE
def check(self):
"""Check if the package is installable."""
self._dbg(3, "check_depends")
# check arch
+ if not "Architecture" 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"):
self._dbg(1, "ERROR: Wrong architecture dude!")
- self._failure_string = _("Wrong architecture '%s'" % arch)
+ self._failure_string = _("Wrong architecture '%s'") % arch
return False
# check version
- if self.compare_to_version_in_cache() == VERSION_OUTDATED:
- # the deb is older than the installed
- self._failure_string = _("A later version is already installed")
- return False
+ if 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")
+ return False
# FIXME: this sort of error handling sux
self._failure_string = ""
@@ -321,6 +399,11 @@ class DebPackage(object):
if not self.check_conflicts():
return False
+ # check if installing it would break anything on the
+ # current system
+ if not self.check_breaks_existing_packages():
+ return False
+
# try to satisfy the dependencies
if not self._satisfy_depends(self.depends):
return False
@@ -332,7 +415,7 @@ class DebPackage(object):
if self._cache._depcache.broken_count > 0:
self._failure_string = _("Failed to satisfy all dependencies "
- "(broken cache)")
+ "(broken cache)")
# clean the cache again
self._cache.clear()
return False
@@ -352,17 +435,16 @@ class DebPackage(object):
# 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)
+ #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
# now try it out in the cache
for pkg in self._need_pkgs:
try:
- self._cache[pkg].mark_install(fromUser=False)
- except SystemError:
- self._failure_string = _("Cannot install '%s'" % pkg)
+ self._cache[pkg].mark_install(from_user=False)
+ except SystemError, e:
+ self._failure_string = _("Cannot install '%s'") % pkg
self._cache.clear()
return False
return True
@@ -398,6 +480,75 @@ class DebPackage(object):
remove.append(pkg.name)
return (install, remove, unauthenticated)
+ @property
+ def control_filelist(self):
+ """ return the list of files in control.tar.gt """
+ try:
+ from debian.debfile import DebFile
+ except:
+ raise Exception(_("Python-debian module not available"))
+ content = []
+ for name in DebFile(self.filename).control:
+ if name and name != ".":
+ content.append(name)
+ return sorted(content)
+
+ def to_hex(self, in_data):
+ hex = ""
+ for (i, c) in enumerate(in_data):
+ if i%80 == 0:
+ hex += "\n"
+ hex += "%2.2x " % ord(c)
+ return hex
+
+ def to_strish(self, in_data):
+ s = ""
+ for c in in_data:
+ if ord(c) < 10 or ord(c) > 127:
+ s += " "
+ else:
+ s += c
+ return s
+
+ def _get_content(self, part, name, auto_decompress=True, auto_hex=True):
+ data = part.get_content(name)
+ # check for zip content
+ if name.endswith(".gz") and auto_decompress:
+ io = StringIO(data)
+ gz = gzip.GzipFile(fileobj=io)
+ data = _("Automatically decompressed:\n\n")
+ data += gz.read()
+ # auto-convert to hex
+ try:
+ data = unicode(data, "utf-8")
+ except Exception, e:
+ new_data = _("Automatically converted to printable ascii:\n")
+ new_data += self.to_strish(data)
+ return new_data
+ return data
+
+ def control_content(self, name):
+ """ return the content of a specific control.tar.gz file """
+ try:
+ from debian.debfile import DebFile
+ except:
+ raise Exception(_("Python-debian module not available"))
+ control = DebFile(self.filename).control
+ if name in control:
+ return self._get_content(control, name)
+ return ""
+
+ def data_content(self, name):
+ """ return the content of a specific control.tar.gz file """
+ try:
+ from debian.debfile import DebFile
+ except:
+ raise Exception(_("Python-debian module not available"))
+ data = DebFile(self.filename).data
+ if name in data:
+ return self._get_content(data, name)
+ return ""
+
def _dbg(self, level, msg):
"""Write debugging output to sys.stderr."""
if level <= self.debug:
@@ -419,18 +570,18 @@ class DebPackage(object):
install_progress.finishUpdate()
return res
-
class DscSrcPackage(DebPackage):
"""A locally available source package."""
def __init__(self, filename=None, cache=None):
DebPackage.__init__(self, None, cache)
+ self.filename = filename
self._depends = []
self._conflicts = []
self.pkgname = ""
self.binaries = []
- if filename is not None:
- self.open(filename)
+ if self.filename is not None:
+ self.open(self.filename)
@property
def depends(self):
@@ -446,7 +597,6 @@ class DscSrcPackage(DebPackage):
"""Open the package."""
depends_tags = ["Build-Depends", "Build-Depends-Indep"]
conflicts_tags = ["Build-Conflicts", "Build-Conflicts-Indep"]
-
fobj = open(file)
tagfile = apt_pkg.TagFile(fobj)
try:
@@ -481,11 +631,10 @@ class DscSrcPackage(DebPackage):
if self._cache[pkgname]._pkg.essential:
raise Exception(_("An essential package would be removed"))
self._cache[pkgname].mark_delete()
- # FIXME: a additional run of the checkConflicts()
- # after _satisfyDepends() should probably be done
+ # FIXME: a additional run of the check_conflicts()
+ # after _satisfy_depends() should probably be done
return self._satisfy_depends(self.depends)
-
def _test():
"""Test function"""
from apt.cache import Cache
@@ -494,7 +643,7 @@ def _test():
cache = Cache()
vp = "www-browser"
- #print "%s virtual: %s" % (vp, cache.isVirtualPackage(vp))
+ print "%s virtual: %s" % (vp, cache.is_virtual_package(vp))
providers = cache.get_providing_packages(vp)
print "Providers for %s :" % vp
for pkg in providers:
@@ -508,6 +657,8 @@ def _test():
print "missing deps: %s" % d.missing_deps
print d.required_changes
+ print d.filelist
+
print "Installing ..."
ret = d.install(DpkgInstallProgress())
print ret
diff --git a/apt/utils.py b/apt/utils.py
index 80ba6d65..4b3da39f 100644
--- a/apt/utils.py
+++ b/apt/utils.py
@@ -33,6 +33,9 @@ def get_maintenance_end_date(release_date, m_months):
support_end_year = (release_date.year + years +
(release_date.month + months)/12)
support_end_month = (release_date.month + months) % 12
+ if support_end_month == 0:
+ support_end_month = 12
+ support_end_year -= 1
return (support_end_year, support_end_month)