From 337fec14957b98c01bc61a5b516bcf8cd98ccb82 Mon Sep 17 00:00:00 2001 From: Martin Pitt Date: Fri, 25 Jun 2010 16:10:13 +0200 Subject: apt/cache.py: When iterating over the cache, do so sorted by package name. With this we read the the package lists linearly if we need to access the package records, instead of having to do thousands of random seeks; the latter is disastrous if we use compressed package indexes, and slower than necessary for uncompressed indexes. --- apt/cache.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/cache.py b/apt/cache.py index 8e07e4d0..3679e4ba 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -64,6 +64,7 @@ class Cache(object): self._callbacks = {} self._weakref = weakref.WeakValueDictionary() self._set = set() + self._sorted_set = None if memonly: # force apt to build its caches in memory apt_pkg.config.set("Dir::Cache::pkgcache", "") @@ -126,6 +127,7 @@ class Cache(object): self._list = apt_pkg.SourceList() self._list.read_main_list() self._set.clear() + self._sorted_set = None self._weakref.clear() progress.op = _("Building data structures") @@ -157,7 +159,15 @@ class Cache(object): raise KeyError('The cache has no package named %r' % key) def __iter__(self): - for pkgname in self._set: + # We iterate sorted over package names here. With this we read the + # package lists linearly if we need to access the package records, + # instead of having to do thousands of random seeks; the latter + # is disastrous if we use compressed package indexes, and slower than + # necessary for uncompressed indexes. + if self._sorted_set is None: + self._sorted_set = sorted(self._set) + + for pkgname in self._sorted_set: yield self[pkgname] raise StopIteration -- cgit v1.2.3 From 0be0102d652ff86a2066088535bfe58cf2d713a4 Mon Sep 17 00:00:00 2001 From: Kiwinote Date: Fri, 25 Jun 2010 17:46:47 +0100 Subject: Merge gdebi changes --- apt/cache.py | 68 ++++++++--- apt/debfile.py | 351 +++++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 310 insertions(+), 109 deletions(-) (limited to 'apt') diff --git a/apt/cache.py b/apt/cache.py index 8e07e4d0..f7a8bbaa 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -1,23 +1,23 @@ # cache.py - apt cache abstraction # -# Copyright (c) 2005-2009 Canonical +# Copyright (c) 2005-2010 Canonical, Ltd. # -# Author: Michael Vogt +# Author: Michael Vogt # -# This program 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 2 of the -# License, or (at your option) any later version. +# This program 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 2 of the +# License, or (at your option) any later version. # -# This program 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. +# This program 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 this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA import os import weakref @@ -84,6 +84,11 @@ class Cache(object): # recognized (LP: #320665) apt_pkg.init_system() self.open(progress) + if progress: + self.op_progress = progress + else: + self.op_progress = apt.progress.base.OpProgress() + def _check_and_create_required_dirs(self, rootdir): """ @@ -198,6 +203,16 @@ class Cache(object): self._depcache.upgrade(dist_upgrade) self.cache_post_change() + def downloadable(self, pkg, use_candidate=True): + """Check if the given package can be downloaded.""" + if use_candidate: + ver = self._depcache.get_candidate_ver(pkg._pkg) + else: + ver = pkg._pkg.current_ver + if ver == None: + return False + return ver.downloadable + @property def required_download(self): """Get the size of the packages that are required to download.""" @@ -306,6 +321,30 @@ class Cache(object): providers.add(package) return list(providers) + def get_providers_for(self, pkgname): + """Return a list of all packages providing a non-virtual package.""" + providers = [] + for pkg in self: + v = self._depcache.get_candidate_ver(pkg._pkg) + if v == None: + continue + for p in v.provides_list: + #print virtual_pkg + #print p[0] + if pkgname == p[0]: + # we found a pkg that provides this virtual + # pkg, check if the proivdes is any good + providers.append(pkg) + #cand = self._cache[pkg.name] + #candver = self._cache._depcache.GetCandidateVer(cand._pkg) + #instver = cand._pkg.CurrentVer + #res = apt_pkg.CheckDep(candver.VerStr,oper,ver) + #if res == True: + # self._dbg(1,"we can use %s" % pkg.name) + # or_found = True + # break + return providers + @deprecated_args def update(self, fetch_progress=None, pulse_interval=0, raise_on_error=True): @@ -474,6 +513,7 @@ class Cache(object): _fetchArchives = function_deprecated_by(_fetch_archives) isVirtualPackage = function_deprecated_by(is_virtual_package) getProvidingPackages = function_deprecated_by(get_providing_packages) + getProvidersFor = function_deprecated_by(get_providers_for) installArchives = function_deprecated_by(install_archives) cachePostChange = function_deprecated_by(cache_post_change) cachePreChange = function_deprecated_by(cache_pre_change) diff --git a/apt/debfile.py b/apt/debfile.py index ccaa25e4..494dd14e 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -1,79 +1,65 @@ -# Copyright (c) 2005-2009 Canonical +# Copyright (c) 2005-2010 Canonical, Ltd. # -# Author: Michael Vogt +# Author: Michael Vogt # -# This program 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 2 of the -# License, or (at your option) any later version. +# This program 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 2 of the +# License, or (at your option) any later version. # -# This program 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. +# This program 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 this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# 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 debian.debfile import DebFile +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 def __init__(self, filename=None, cache=None): + if cache: + cache.clear() self._cache = cache + self.file = filename self._need_pkgs = [] + self._sections = {} self._debfile = None self.pkgname = "" - self.filename = filename - self._sections = {} self._installed_conflicts = set() self._failure_string = "" - if filename: - self.open(filename) + if self.file: + self.open(self.file) - def open(self, filename): + def open(self, file): " open given debfile " - self.filename = filename - self._debfile = apt_inst.DebFile(self.filename) - control = self._debfile.control.extractdata("control") + control = apt_inst.debExtractControl(open(file)) self._sections = apt_pkg.TagSection(control) self.pkgname = self._sections["Package"] - def __getitem__(self, key): - return self._sections[key] - - @property - def filelist(self): - """return the list of files in the deb.""" - files = [] - try: - self._debfile.data.go(lambda item, data: files.append(item.name)) - except SystemError: - return [_("List of files for '%s' could not be read" % - self.filename)] - return files - def _is_or_group_satisfied(self, or_group): """Return True if at least one dependency of the or-group is satisfied. @@ -90,20 +76,34 @@ 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_providers_for(depname): + 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 +136,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 @@ -162,8 +161,7 @@ class DebPackage(object): #print "oper: %s " % oper 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) + self._failure_string += _("Conflicts with the installed package '%s'") % pkg.name return True return False @@ -171,6 +169,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,8 +188,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, - 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): @@ -209,7 +209,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 +240,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 @@ -262,6 +262,19 @@ class DebPackage(object): Check if the package conflicts with a existing or to be installed package. Return True if the pkg is OK. """ + for con in self.conflicts: + for pro in self.provides: + if con[0][0] == pro[0][0]: + if con[0][2]: + pro_ver = self._sections["Version"] + if apt_pkg.check_dep(pro_ver, con[0][1], con[0][2]): + #print "Conflicts with provided pkg!" + self._failure_string = "Conflicts with a provided package" + return False + else: + #print "Conflicts with provided pkg!" + self._failure_string = "Conflicts with a provided package" + return False res = True for or_group in self.conflicts: if self._check_conflicts_or_group(or_group): @@ -270,6 +283,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,7 +341,7 @@ 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) @@ -287,32 +351,37 @@ class DebPackage(object): else: cachever = self._cache[pkgname].candidate.version 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 +390,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 @@ -331,8 +405,7 @@ class DebPackage(object): return False if self._cache._depcache.broken_count > 0: - self._failure_string = _("Failed to satisfy all dependencies " - "(broken cache)") + self._failure_string = _("Failed to satisfy all dependencies (broken cache)") # clean the cache again self._cache.clear() return False @@ -352,19 +425,18 @@ 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.clear() - return False + for pkg in self._need_pkgs: + try: + 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 @property @@ -398,6 +470,95 @@ class DebPackage(object): remove.append(pkg.name) return (install, remove, unauthenticated) + def control_filelist(self): + """ return the list of files in control.tar.gt """ + content = [] + for name in DebFile(self.file).control: + if name and name != ".": + content.append(name) + return sorted(content) + control_filelist = property(control_filelist) + + 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 """ + control = DebFile(self.file).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 """ + data = DebFile(self.file).data + if name in data: + return self._get_content(data, name) + return "" + + @property + def filelist(self): + """ return the list of files in the deb. """ + files = [] + def extract_cb(What,Name,Link,Mode,UID,GID,Size,MTime,Major,Minor): + #print "%s '%s','%s',%u,%u,%u,%u,%u,%u,%u"\ + # % (What,Name,Link,Mode,UID,GID,Size, MTime, Major, Minor) + if Name != "./": + files.append(Name) + try: + try: + apt_inst.debExtract(open(self.file), extract_cb, "data.tar.gz") + except SystemError, e: + try: + apt_inst.debExtract(open(self.file), extract_cb, "data.tar.bz2") + except SystemError, e: + try: + apt_inst.debExtract(open(self.file), extract_cb, "data.tar.lzma") + except SystemError, e: + return [_("List of files could not be read, please report this as a bug")] + # IOError may happen because of gvfs madness (LP: #211822) + except IOError, e: + return [_("IOError during filelist read: %s") % e] + return files + + def __getitem__(self,item): + if not item in self._sections: + # Translators: it's for missing entries in the deb package, + # e.g. a missing "Maintainer" field + return _("%s is not available") % item + return self._sections[item] + def _dbg(self, level, msg): """Write debugging output to sys.stderr.""" if level <= self.debug: @@ -406,31 +567,31 @@ class DebPackage(object): def install(self, install_progress=None): """Install the package.""" if install_progress is None: - return os.spawnlp(os.P_WAIT, "dpkg", "dpkg", "-i", self.filename) + return os.spawnlp(os.P_WAIT, "dpkg", "dpkg", "-i", self.file) else: try: install_progress.start_update() except AttributeError: install_progress.startUpdate() - res = install_progress.run(self.filename) + res = install_progress.run(self.file) try: install_progress.finish_update() except AttributeError: 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.file = filename self._depends = [] self._conflicts = [] self.pkgname = "" self.binaries = [] - if filename is not None: - self.open(filename) + if self.file is not None: + self.open(self.file) @property def depends(self): @@ -446,7 +607,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 +641,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 +653,7 @@ def _test(): cache = Cache() vp = "www-browser" - #print "%s virtual: %s" % (vp, cache.isVirtualPackage(vp)) + print "%s virtual: %s" % (vp, cache.is_virtual_pkg(vp)) providers = cache.get_providing_packages(vp) print "Providers for %s :" % vp for pkg in providers: @@ -508,6 +667,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 -- cgit v1.2.3 From 45a7ba935c89034c0ccc0c9ccc4f801a3d465bcf Mon Sep 17 00:00:00 2001 From: Kiwinote Date: Fri, 25 Jun 2010 19:07:16 +0100 Subject: Remove unneeded cache.downloadable() --- apt/cache.py | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'apt') diff --git a/apt/cache.py b/apt/cache.py index f7a8bbaa..e50940ce 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -203,16 +203,6 @@ class Cache(object): self._depcache.upgrade(dist_upgrade) self.cache_post_change() - def downloadable(self, pkg, use_candidate=True): - """Check if the given package can be downloaded.""" - if use_candidate: - ver = self._depcache.get_candidate_ver(pkg._pkg) - else: - ver = pkg._pkg.current_ver - if ver == None: - return False - return ver.downloadable - @property def required_download(self): """Get the size of the packages that are required to download.""" -- cgit v1.2.3 From ae363bccb5b52ffc7a17e3f6b3d7b1cff5f65a98 Mon Sep 17 00:00:00 2001 From: Kiwinote Date: Fri, 25 Jun 2010 21:11:57 +0100 Subject: Merge cache.get_providers_for() into cache.get_providing_packages() and update debfile.py to use this --- apt/cache.py | 90 ++++++++++++++++++++++++++++------------------------------ apt/debfile.py | 4 +-- 2 files changed, 46 insertions(+), 48 deletions(-) (limited to 'apt') diff --git a/apt/cache.py b/apt/cache.py index e50940ce..98241379 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -282,59 +282,57 @@ 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): + """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. + Return a list of packages which provide the package of the specified + name. For virtual packages, if 'candidate_only' is False, return all + packages with at least one version providing the package. Otherwise, + return only those packages where the candidate version provides the + package. """ providers = set() - get_candidate_ver = self._depcache.get_candidate_ver - try: - vp = self._cache[virtual] - if vp.has_versions: + virtual = self.is_virtual_package(pkgname) + if not virtual: + for pkg in self: + v = self._depcache.get_candidate_ver(pkg._pkg) + if v == None: + continue + for p in v.provides_list: + #print virtual_pkg + #print p[0] + if pkgname == p[0]: + # we found a pkg that provides this virtual + # pkg, check if the proivdes is any good + providers.add(pkg) + #cand = self._cache[pkg.name] + #candver = self._cache._depcache.GetCandidateVer(cand._pkg) + #instver = cand._pkg.CurrentVer + #res = apt_pkg.CheckDep(candver.VerStr,oper,ver) + #if res == True: + # self._dbg(1,"we can use %s" % pkg.name) + # or_found = True + # break + else: + get_candidate_ver = self._depcache.get_candidate_ver + try: + vp = self._cache[pkgname] + if vp.has_versions: + return list(providers) + except KeyError: return list(providers) - except KeyError: - return list(providers) - for provides, providesver, version in vp.provides_list: - pkg = version.parent_pkg - if not candidate_only or (version == get_candidate_ver(pkg)): - try: - providers.add(self._weakref[pkg.name]) - except KeyError: - package = self._weakref[pkg.name] = Package(self, pkg) - providers.add(package) + for provides, providesver, version in vp.provides_list: + pkg = version.parent_pkg + if not candidate_only or (version == get_candidate_ver(pkg)): + try: + providers.add(self._weakref[pkg.name]) + except KeyError: + package = self._weakref[pkg.name] = Package(self, pkg) + providers.add(package) return list(providers) - def get_providers_for(self, pkgname): - """Return a list of all packages providing a non-virtual package.""" - providers = [] - for pkg in self: - v = self._depcache.get_candidate_ver(pkg._pkg) - if v == None: - continue - for p in v.provides_list: - #print virtual_pkg - #print p[0] - if pkgname == p[0]: - # we found a pkg that provides this virtual - # pkg, check if the proivdes is any good - providers.append(pkg) - #cand = self._cache[pkg.name] - #candver = self._cache._depcache.GetCandidateVer(cand._pkg) - #instver = cand._pkg.CurrentVer - #res = apt_pkg.CheckDep(candver.VerStr,oper,ver) - #if res == True: - # self._dbg(1,"we can use %s" % pkg.name) - # or_found = True - # break - return providers - @deprecated_args def update(self, fetch_progress=None, pulse_interval=0, raise_on_error=True): @@ -503,7 +501,7 @@ class Cache(object): _fetchArchives = function_deprecated_by(_fetch_archives) isVirtualPackage = function_deprecated_by(is_virtual_package) getProvidingPackages = function_deprecated_by(get_providing_packages) - getProvidersFor = function_deprecated_by(get_providers_for) + getProvidersFor = function_deprecated_by(get_providing_packages) installArchives = function_deprecated_by(install_archives) cachePostChange = function_deprecated_by(cache_post_change) cachePreChange = function_deprecated_by(cache_pre_change) diff --git a/apt/debfile.py b/apt/debfile.py index 494dd14e..90d58449 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -92,7 +92,7 @@ class DebPackage(object): # 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_providers_for(depname): + for ppkg in self._cache.get_providing_packages(depname): if ppkg.is_installed: self._dbg(3, "found installed '%s' that provides '%s'" % (ppkg.name, depname)) return True @@ -653,7 +653,7 @@ def _test(): cache = Cache() vp = "www-browser" - print "%s virtual: %s" % (vp, cache.is_virtual_pkg(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: -- cgit v1.2.3 From 5ced3fd6f12111305f43f8cdc9b04a5b3bc2be7e Mon Sep 17 00:00:00 2001 From: Kiwinote Date: Fri, 25 Jun 2010 21:45:19 +0100 Subject: Don't depend on python-debian --- apt/debfile.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 90d58449..469cbb36 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -24,7 +24,6 @@ import os import sys from apt_pkg import gettext as _ -from debian.debfile import DebFile from StringIO import StringIO class NoDebArchiveException(IOError): @@ -470,14 +469,18 @@ 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.file).control: if name and name != ".": content.append(name) return sorted(content) - control_filelist = property(control_filelist) def to_hex(self, in_data): hex = "" @@ -515,6 +518,10 @@ class DebPackage(object): 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.file).control if name in control: return self._get_content(control, name) @@ -522,6 +529,10 @@ class DebPackage(object): 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.file).data if name in data: return self._get_content(data, name) -- cgit v1.2.3 From 70da3b041dcb31e783c381d002fdee8bba9ec292 Mon Sep 17 00:00:00 2001 From: Kiwinote Date: Mon, 28 Jun 2010 09:22:23 +0100 Subject: Don't query cache[].candidate.version when no cache[].candidate is available --- apt/debfile.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 469cbb36..f74b6981 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -347,8 +347,10 @@ class DebPackage(object): 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: cmp = apt_pkg.version_compare(cachever, debver) self._dbg(1, "CompareVersion(debver,instver): %s" % cmp) -- cgit v1.2.3 From dc53e59cb9718fd5751066539fed46d7cdf01268 Mon Sep 17 00:00:00 2001 From: Kiwinote Date: Mon, 28 Jun 2010 09:54:06 +0100 Subject: December is month 12, not month 0 --- apt/utils.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'apt') diff --git a/apt/utils.py b/apt/utils.py index 80ba6d65..f4d34261 100644 --- a/apt/utils.py +++ b/apt/utils.py @@ -33,6 +33,8 @@ 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 return (support_end_year, support_end_month) -- cgit v1.2.3 From 944626c874a7b42043d279d918c92c407627a314 Mon Sep 17 00:00:00 2001 From: Kiwinote Date: Mon, 28 Jun 2010 10:02:06 +0100 Subject: And subtract a year --- apt/utils.py | 1 + 1 file changed, 1 insertion(+) (limited to 'apt') diff --git a/apt/utils.py b/apt/utils.py index f4d34261..4b3da39f 100644 --- a/apt/utils.py +++ b/apt/utils.py @@ -35,6 +35,7 @@ def get_maintenance_end_date(release_date, m_months): 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) -- cgit v1.2.3 From 1c66ccad17b32488aaac0a78016f5378a789bfb1 Mon Sep 17 00:00:00 2001 From: Kiwinote Date: Mon, 28 Jun 2010 11:14:20 +0100 Subject: self.conflicts returns name, ver, oper, rather than name, oper, ver --- apt/debfile.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index f74b6981..1c33d25f 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -261,12 +261,12 @@ class DebPackage(object): Check if the package conflicts with a existing or to be installed package. Return True if the pkg is OK. """ - for con in self.conflicts: + for [(con_name, con_ver, con_oper)] in self.conflicts: for pro in self.provides: - if con[0][0] == pro[0][0]: - if con[0][2]: + if con_name == pro[0][0]: + if con_ver: pro_ver = self._sections["Version"] - if apt_pkg.check_dep(pro_ver, con[0][1], con[0][2]): + if apt_pkg.check_dep(pro_ver, con_oper, con_ver): #print "Conflicts with provided pkg!" self._failure_string = "Conflicts with a provided package" return False -- cgit v1.2.3 From 0c4bbfa36f36d42cc3d700e58d531e05ba898e0e Mon Sep 17 00:00:00 2001 From: Kiwinote Date: Mon, 28 Jun 2010 12:54:51 +0100 Subject: Revert own change, as we can conflict/provide the same package --- apt/debfile.py | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 1c33d25f..c0ae4e28 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -261,19 +261,6 @@ class DebPackage(object): Check if the package conflicts with a existing or to be installed package. Return True if the pkg is OK. """ - for [(con_name, con_ver, con_oper)] in self.conflicts: - for pro in self.provides: - if con_name == pro[0][0]: - if con_ver: - pro_ver = self._sections["Version"] - if apt_pkg.check_dep(pro_ver, con_oper, con_ver): - #print "Conflicts with provided pkg!" - self._failure_string = "Conflicts with a provided package" - return False - else: - #print "Conflicts with provided pkg!" - self._failure_string = "Conflicts with a provided package" - return False res = True for or_group in self.conflicts: if self._check_conflicts_or_group(or_group): -- cgit v1.2.3 From 508f1ddb5b6a0cc69655e39dbd59fe3466173a84 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 29 Jun 2010 11:23:31 +0200 Subject: * apt/cache.py: - add new "dpkg_journal_dirty" property that can be used to detect a interrupted dpkg (the famous "E: dpkg was interrupted, you must manually run 'dpkg --configure -a'") --- apt/cache.py | 15 +++++++++++++++ debian/changelog | 4 ++++ tests/test_apt_cache.py | 26 ++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) (limited to 'apt') diff --git a/apt/cache.py b/apt/cache.py index 3679e4ba..3962bb4f 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -19,6 +19,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA +import fnmatch import os import weakref @@ -454,6 +455,20 @@ class Cache(object): """ return apt_pkg.ActionGroup(self._depcache) + @property + def dpkg_journal_dirty(self): + """Return True if the dpkg was interrupted + + All dpkg operations will fail until this is fixed, the action to + fix the system if dpkg got interrupted is to run + 'dpkg --configure -a' as root. + """ + dpkg_status_dir = os.path.dirname(apt_pkg.Config.find_file("Dir::State::status")) + for f in os.listdir(os.path.join(dpkg_status_dir, "updates")): + if fnmatch.fnmatch(f, "[0-9]*"): + return True + return False + @property def broken_count(self): """Return the number of packages with broken dependencies.""" diff --git a/debian/changelog b/debian/changelog index b7bb3ad0..b1c7c1b5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -11,6 +11,10 @@ python-apt (0.7.96) UNRELEASED; urgency=low - do not fail on non-digits in the version number * utils/get_debian_mirrors.py: - ignore mirrors without a county + * apt/cache.py: + - add new "dpkg_journal_dirty" property that can be used to + detect a interrupted dpkg (the famous + "E: dpkg was interrupted, you must manually run 'dpkg --configure -a'") [ Martin Pitt ] * tests/test_apt_cache.py: Test accessing the record of all packages during diff --git a/tests/test_apt_cache.py b/tests/test_apt_cache.py index a00fa08b..b27ed778 100644 --- a/tests/test_apt_cache.py +++ b/tests/test_apt_cache.py @@ -9,6 +9,9 @@ import unittest import apt +import apt_pkg +import os +import tempfile class TestAptCache(unittest.TestCase): @@ -39,5 +42,28 @@ class TestAptCache(unittest.TestCase): self.assert_(len(r['Description']) > 0) self.assert_(str(r).startswith('Package: %s\n' % pkg.name)) + def test_dpkg_journal_dirty(self): + # backup old value + old_status = apt_pkg.Config.find_file("Dir::State::status") + # create tmp env + tmpdir = tempfile.mkdtemp() + dpkg_dir = os.path.join(tmpdir,"var","lib","dpkg") + os.makedirs(os.path.join(dpkg_dir,"updates")) + open(os.path.join(dpkg_dir,"status"), "w") + apt_pkg.Config.set("Dir::State::status", + os.path.join(dpkg_dir,"status")) + cache = apt.Cache() + # test empty + self.assertFalse(cache.dpkg_journal_dirty) + # that is ok, only [0-9] are dpkg jounral entries + open(os.path.join(dpkg_dir,"updates","xxx"), "w") + self.assertFalse(cache.dpkg_journal_dirty) + # that is a dirty journal + open(os.path.join(dpkg_dir,"updates","000"), "w") + self.assertTrue(cache.dpkg_journal_dirty) + # reset config value + apt_pkg.Config.set("Dir::State::status", old_status) + + if __name__ == "__main__": unittest.main() -- cgit v1.2.3 From 7dd530ec0cfd871d4b2b21c88f17aebca29b3dcd Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Fri, 2 Jul 2010 15:25:05 +0200 Subject: apt/debfile.py: DebFile needs a open file --- apt/debfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 83a5d68c..528cd311 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -58,7 +58,7 @@ class DebPackage(object): def open(self, filename): """ open given debfile """ self.filename = filename - self._debfile = apt_inst.DebFile(self.filename) + self._debfile = apt_inst.DebFile(open(self.filename)) control = self._debfile.control.extractdata("control") self._sections = apt_pkg.TagSection(control) self.pkgname = self._sections["Package"] -- cgit v1.2.3 From 850c486cc0c96f2a36a2b405354163fe41d356e7 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Fri, 2 Jul 2010 15:34:30 +0200 Subject: apt/debfile.py: fix bug in compare_to_version_in_cache with use_installed=True --- apt/debfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 528cd311..3b3a8d97 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -355,7 +355,7 @@ class DebPackage(object): if pkgname in self._cache: if use_installed and self._cache[pkgname].installed: cachever = self._cache[pkgname].installed.version - elif self._cache[pkgname].candidate: + elif not use_installed and self._cache[pkgname].candidate: cachever = self._cache[pkgname].candidate.version else: return self.VERSION_NONE -- cgit v1.2.3 From 945084be45a95fe532a6669e2c85a0ba363ecfd9 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Fri, 2 Jul 2010 16:12:13 +0200 Subject: * apt/debfile.py: - check if the debfiles provides are in conflict with the systems packages --- apt/debfile.py | 13 ++++++++++++- debian/changelog | 3 +++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 3b3a8d97..173521b1 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -301,6 +301,8 @@ class DebPackage(object): size = float(len(self._cache)) steps = int(size/50) debver = self._sections["Version"] + # store what we provide so that we can later check against that + 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) @@ -331,13 +333,22 @@ class DebPackage(object): 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)") % { + 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: + 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() return True diff --git a/debian/changelog b/debian/changelog index 69db957f..29f37932 100644 --- a/debian/changelog +++ b/debian/changelog @@ -18,6 +18,9 @@ python-apt (0.7.96) UNRELEASED; urgency=low * 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 + * apt/debfile.py: + - check if the debfiles provides are in conflict with the systems + packages [ Martin Pitt ] * tests/test_apt_cache.py: Test accessing the record of all packages during -- cgit v1.2.3 From dd86db9ddb22b43867b5b4dc211f4440dec7aa6a Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Fri, 2 Jul 2010 16:37:45 +0200 Subject: * tests/test_debs/*.deb, tests/test_debfile.py: - add automatic test based on the test debs from gdebi --- apt/debfile.py | 10 ++++--- debian/changelog | 2 ++ tests/test_debfile.py | 56 +++++++++++++++++++++++++++++++++++++++ tests/test_debs/gdebi-test1.deb | Bin 0 -> 934 bytes tests/test_debs/gdebi-test10.deb | Bin 0 -> 614 bytes tests/test_debs/gdebi-test2.deb | Bin 0 -> 554 bytes tests/test_debs/gdebi-test3.deb | Bin 0 -> 570 bytes tests/test_debs/gdebi-test4.deb | Bin 0 -> 2306 bytes tests/test_debs/gdebi-test5.deb | Bin 0 -> 2306 bytes tests/test_debs/gdebi-test6.deb | Bin 0 -> 4312 bytes tests/test_debs/gdebi-test7.deb | Bin 0 -> 578 bytes tests/test_debs/gdebi-test8.deb | Bin 0 -> 598 bytes tests/test_debs/gdebi-test9.deb | Bin 0 -> 586 bytes 13 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 tests/test_debfile.py create mode 100644 tests/test_debs/gdebi-test1.deb create mode 100644 tests/test_debs/gdebi-test10.deb create mode 100644 tests/test_debs/gdebi-test2.deb create mode 100644 tests/test_debs/gdebi-test3.deb create mode 100644 tests/test_debs/gdebi-test4.deb create mode 100644 tests/test_debs/gdebi-test5.deb create mode 100644 tests/test_debs/gdebi-test6.deb create mode 100644 tests/test_debs/gdebi-test7.deb create mode 100644 tests/test_debs/gdebi-test8.deb create mode 100644 tests/test_debs/gdebi-test9.deb (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 173521b1..81cd1dd2 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -17,6 +17,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA """Classes for working with locally available Debian packages.""" +import apt import apt_inst import apt_pkg import gzip @@ -44,19 +45,20 @@ class DebPackage(object): debug = 0 def __init__(self, filename=None, cache=None): + if cache is None: + cache = apt.Cache() self._cache = cache - self._need_pkgs = [] self._debfile = None self.pkgname = "" - self.filename = filename self._sections = {} - self._installed_conflicts = set() - self._failure_string = "" if filename: self.open(filename) def open(self, filename): """ open given debfile """ + self._need_pkgs = [] + self._installed_conflicts = set() + self._failure_string = "" self.filename = filename self._debfile = apt_inst.DebFile(open(self.filename)) control = self._debfile.control.extractdata("control") diff --git a/debian/changelog b/debian/changelog index 29f37932..b8ae49c1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -21,6 +21,8 @@ python-apt (0.7.96) UNRELEASED; urgency=low * apt/debfile.py: - check if the debfiles provides are in conflict with the systems packages + * tests/test_debs/*.deb, tests/test_debfile.py: + - add automatic test based on the test debs from gdebi [ Martin Pitt ] * tests/test_apt_cache.py: Test accessing the record of all packages during diff --git a/tests/test_debfile.py b/tests/test_debfile.py new file mode 100644 index 00000000..02e25117 --- /dev/null +++ b/tests/test_debfile.py @@ -0,0 +1,56 @@ +#!/usr/bin/python +# +# Copyright (C) 2010 Michael Vogt +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. +"""Unit tests for verifying the correctness of DebPackage in apt.debfile.""" +import os +import logging +import unittest + +import sys +sys.path.insert(0, "..") + +import apt.debfile + + +class TestDebfilee(unittest.TestCase): + """ test the apt cache """ + + TEST_DEBS = [ + # conflicts with apt + ('gdebi-test1.deb', False), + # impossible dependency + ('gdebi-test2.deb', False), + # or-group (impossible-dependency|apt) + ('gdebi-test3.deb', True), + # Conflicts: apt (<= 0.1) + ('gdebi-test4.deb', True), + # Conflicts: apt (>= 0.1) + ('gdebi-test5.deb', False), + # invalid unicode in descr + ('gdebi-test6.deb', True), + # provides/conflicts against "foobarbaz" + ('gdebi-test7.deb', True), + # provides/conflicts/replaces against "mail-transport-agent" + # (should fails if mail-transport-agent is installed) + ('gdebi-test8.deb', False), + # provides/conflicts against real pkg + ('gdebi-test9.deb', True), + # provides debconf-tiny and the real debconf conflicts with + ('gdebi-test10.deb', False), + ] + + def testDebFile(self): + deb = apt.debfile.DebPackage() + for (filename, expected_res) in self.TEST_DEBS: + logging.debug("testing %s, expecting %s" % (filename, expected_res)) + deb.open(os.path.join("test_debs", filename)) + res = deb.check() + self.assertEqual(res, expected_res) + +if __name__ == "__main__": + #logging.basicConfig(level=logging.DEBUG) + unittest.main() diff --git a/tests/test_debs/gdebi-test1.deb b/tests/test_debs/gdebi-test1.deb new file mode 100644 index 00000000..ea9991ac Binary files /dev/null and b/tests/test_debs/gdebi-test1.deb differ diff --git a/tests/test_debs/gdebi-test10.deb b/tests/test_debs/gdebi-test10.deb new file mode 100644 index 00000000..ca43ace6 Binary files /dev/null and b/tests/test_debs/gdebi-test10.deb differ diff --git a/tests/test_debs/gdebi-test2.deb b/tests/test_debs/gdebi-test2.deb new file mode 100644 index 00000000..307ac689 Binary files /dev/null and b/tests/test_debs/gdebi-test2.deb differ diff --git a/tests/test_debs/gdebi-test3.deb b/tests/test_debs/gdebi-test3.deb new file mode 100644 index 00000000..436b9258 Binary files /dev/null and b/tests/test_debs/gdebi-test3.deb differ diff --git a/tests/test_debs/gdebi-test4.deb b/tests/test_debs/gdebi-test4.deb new file mode 100644 index 00000000..9eb92d1b Binary files /dev/null and b/tests/test_debs/gdebi-test4.deb differ diff --git a/tests/test_debs/gdebi-test5.deb b/tests/test_debs/gdebi-test5.deb new file mode 100644 index 00000000..0c98c03f Binary files /dev/null and b/tests/test_debs/gdebi-test5.deb differ diff --git a/tests/test_debs/gdebi-test6.deb b/tests/test_debs/gdebi-test6.deb new file mode 100644 index 00000000..32fd1800 Binary files /dev/null and b/tests/test_debs/gdebi-test6.deb differ diff --git a/tests/test_debs/gdebi-test7.deb b/tests/test_debs/gdebi-test7.deb new file mode 100644 index 00000000..c0414990 Binary files /dev/null and b/tests/test_debs/gdebi-test7.deb differ diff --git a/tests/test_debs/gdebi-test8.deb b/tests/test_debs/gdebi-test8.deb new file mode 100644 index 00000000..439f8ca7 Binary files /dev/null and b/tests/test_debs/gdebi-test8.deb differ diff --git a/tests/test_debs/gdebi-test9.deb b/tests/test_debs/gdebi-test9.deb new file mode 100644 index 00000000..9901d906 Binary files /dev/null and b/tests/test_debs/gdebi-test9.deb differ -- cgit v1.2.3 From 750c50bd08d909b5802f892c88441000609183c0 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Mon, 5 Jul 2010 18:28:48 +0200 Subject: apt/debfile.py: make to_{hex,strish} staticmethods --- apt/debfile.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 81cd1dd2..06854cf2 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -506,6 +506,7 @@ class DebPackage(object): content.append(name) return sorted(content) + @staticmethod def to_hex(self, in_data): hex = "" for (i, c) in enumerate(in_data): @@ -514,6 +515,7 @@ class DebPackage(object): hex += "%2.2x " % ord(c) return hex + @staticmethod def to_strish(self, in_data): s = "" for c in in_data: -- cgit v1.2.3 From b94455e7afd0e08ce1a8aab890dbee0d42566d7b Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 6 Jul 2010 09:45:16 +0200 Subject: fix debfile to work with py3, update tests --- apt/debfile.py | 3 ++- tests/test_all.py | 32 ++++++++++++++++++++------------ tests/test_debfile.py | 3 ++- tests/test_debs/gdebi-test6.deb | Bin 4312 -> 600 bytes 4 files changed, 24 insertions(+), 14 deletions(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 81cd1dd2..16d1b15e 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -62,7 +62,8 @@ class DebPackage(object): self.filename = filename self._debfile = apt_inst.DebFile(open(self.filename)) control = self._debfile.control.extractdata("control") - self._sections = apt_pkg.TagSection(control) + # hm, 'replace' is probably better but python2.6 test fail with that + self._sections = apt_pkg.TagSection(control.decode("UTF-8", 'ignore')) self.pkgname = self._sections["Package"] def __getitem__(self, key): diff --git a/tests/test_all.py b/tests/test_all.py index d561a9ae..d6370747 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -9,21 +9,29 @@ import os import unittest import sys -if __name__ == '__main__': - sys.stderr.write("[tests] Running on %s\n" % sys.version.replace("\n", "")) - os.chdir(os.path.dirname(__file__)) +def get_library_dir(): # Find the path to the built apt_pkg and apt_inst extensions - if os.path.exists("../build"): - from distutils.util import get_platform - from distutils.sysconfig import get_python_version - # Set the path to the build directory. - plat_specifier = ".%s-%s" % (get_platform(), get_python_version()) - if sys.version_info[0] >= 3 or sys.version_info[1] >= 6: - library_dir = "../build/lib%s%s" % (plat_specifier, + if not os.path.exists("../build"): + return None + from distutils.util import get_platform + from distutils.sysconfig import get_python_version + # Set the path to the build directory. + plat_specifier = ".%s-%s" % (get_platform(), get_python_version()) + if sys.version_info[0] >= 3 or sys.version_info[1] >= 6: + library_dir = "../build/lib%s%s" % (plat_specifier, (sys.pydebug and "-pydebug" or "")) - else: - library_dir = "../build/lib%s%s" % ((sys.pydebug and "_d" or ""), + else: + library_dir = "../build/lib%s%s" % ((sys.pydebug and "_d" or ""), plat_specifier) + return os.path.abspath(library_dir) + +if __name__ == '__main__': + sys.stderr.write("[tests] Running on %s\n" % sys.version.replace("\n", "")) + dirname = os.path.dirname(__file__) + if dirname: + os.chdir(dirname) + library_dir = get_library_dir() + if library_dir: sys.path.insert(0, os.path.abspath(library_dir)) for path in os.listdir('.'): diff --git a/tests/test_debfile.py b/tests/test_debfile.py index 02e25117..56bbba9f 100644 --- a/tests/test_debfile.py +++ b/tests/test_debfile.py @@ -10,8 +10,9 @@ import os import logging import unittest +from test_all import get_library_dir import sys -sys.path.insert(0, "..") +sys.path.insert(0, get_library_dir()) import apt.debfile diff --git a/tests/test_debs/gdebi-test6.deb b/tests/test_debs/gdebi-test6.deb index 32fd1800..8ceacadc 100644 Binary files a/tests/test_debs/gdebi-test6.deb and b/tests/test_debs/gdebi-test6.deb differ -- cgit v1.2.3 From dd58eafe47bb69797966f70f1181e4046856cfe4 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 6 Jul 2010 10:47:00 +0200 Subject: apt/cache.py: use apt_pkg.config instead of apt_pkg.Config --- apt/cache.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/cache.py b/apt/cache.py index 26532c76..f64b489a 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -471,7 +471,8 @@ class Cache(object): fix the system if dpkg got interrupted is to run 'dpkg --configure -a' as root. """ - dpkg_status_dir = os.path.dirname(apt_pkg.Config.find_file("Dir::State::status")) + dpkg_status_dir = os.path.dirname( + apt_pkg.config.find_file("Dir::State::status")) for f in os.listdir(os.path.join(dpkg_status_dir, "updates")): if fnmatch.fnmatch(f, "[0-9]*"): return True -- cgit v1.2.3 From f75c59b6b250d14b1e52e1eebebfb5b11482d2ef Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Mon, 12 Jul 2010 14:10:40 +0200 Subject: tests/test_debfile.py: add fixture dpkg-status file to make tests work --- apt/debfile.py | 2 +- po/python-apt.pot | 58 ++++++++++++++++++++++++--------------------------- tests/test_debfile.py | 15 +++++++++---- 3 files changed, 39 insertions(+), 36 deletions(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 16d1b15e..7a1fa82d 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -302,7 +302,7 @@ class DebPackage(object): """ # show progress information as this step may take some time size = float(len(self._cache)) - steps = int(size/50) + steps = max(int(size/50), 1) debver = self._sections["Version"] # store what we provide so that we can later check against that provides = [ x[0][0] for x in self.provides] diff --git a/po/python-apt.pot b/po/python-apt.pot index 9c10c43f..036495e2 100644 --- a/po/python-apt.pot +++ b/po/python-apt.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-07-02 14:12+0200\n" +"POT-Creation-Date: 2010-07-12 14:07+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -413,18 +413,23 @@ msgid "" "Please check your Internet connection." msgstr "" -#: ../apt/debfile.py:142 +#: ../apt/debfile.py:79 +#, python-format +msgid "List of files for '%s' could not be read" +msgstr "" + +#: ../apt/debfile.py:164 #, python-format msgid "Dependency is not satisfiable: %s\n" msgstr "" -#: ../apt/debfile.py:163 +#: ../apt/debfile.py:185 #, python-format msgid "Conflicts with the installed package '%s'" msgstr "" #. 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 -#: ../apt/debfile.py:297 +#: ../apt/debfile.py:323 #, python-format msgid "" "Breaks existing package '%(pkgname)s' dependency %(depname)s (%(deprelation)" @@ -432,72 +437,63 @@ msgid "" msgstr "" #. 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 -#: ../apt/debfile.py:313 +#: ../apt/debfile.py:339 #, python-format msgid "" "Breaks existing package '%(pkgname)s' conflict: %(targetpkg)s (%(comptype)s %" "(targetver)s)" msgstr "" -#: ../apt/debfile.py:359 +#: ../apt/debfile.py:348 +#, python-format +msgid "" +"Breaks existing package '%(pkgname)s' that conflict: '%(targetpkg)s'. But " +"the '%(debfile)s' provides it via: '%(provides)s'" +msgstr "" + +#: ../apt/debfile.py:394 msgid "No Architecture field in the package" msgstr "" -#: ../apt/debfile.py:364 +#: ../apt/debfile.py:399 #, python-format msgid "Wrong architecture '%s'" msgstr "" #. the deb is older than the installed -#: ../apt/debfile.py:371 +#: ../apt/debfile.py:406 msgid "A later version is already installed" msgstr "" -#: ../apt/debfile.py:396 +#: ../apt/debfile.py:431 msgid "Failed to satisfy all dependencies (broken cache)" msgstr "" -#: ../apt/debfile.py:425 +#: ../apt/debfile.py:461 #, python-format msgid "Cannot install '%s'" msgstr "" -#: ../apt/debfile.py:467 ../apt/debfile.py:513 ../apt/debfile.py:524 +#: ../apt/debfile.py:503 ../apt/debfile.py:549 ../apt/debfile.py:560 msgid "Python-debian module not available" msgstr "" -#: ../apt/debfile.py:497 +#: ../apt/debfile.py:533 msgid "" "Automatically decompressed:\n" "\n" msgstr "" -#: ../apt/debfile.py:503 +#: ../apt/debfile.py:539 msgid "Automatically converted to printable ascii:\n" msgstr "" -#: ../apt/debfile.py:549 -msgid "List of files could not be read, please report this as a bug" -msgstr "" - -#: ../apt/debfile.py:552 -#, python-format -msgid "IOError during filelist read: %s" -msgstr "" - -#. Translators: it's for missing entries in the deb package, -#. e.g. a missing "Maintainer" field -#: ../apt/debfile.py:559 -#, python-format -msgid "%s is not available" -msgstr "" - -#: ../apt/debfile.py:632 +#: ../apt/debfile.py:636 #, python-format msgid "Install Build-Dependencies for source package '%s' that builds %s\n" msgstr "" -#: ../apt/debfile.py:642 +#: ../apt/debfile.py:646 msgid "An essential package would be removed" msgstr "" diff --git a/tests/test_debfile.py b/tests/test_debfile.py index 56bbba9f..6af0cf1c 100644 --- a/tests/test_debfile.py +++ b/tests/test_debfile.py @@ -13,10 +13,9 @@ import unittest from test_all import get_library_dir import sys sys.path.insert(0, get_library_dir()) - +import apt_pkg import apt.debfile - class TestDebfilee(unittest.TestCase): """ test the apt cache """ @@ -44,13 +43,21 @@ class TestDebfilee(unittest.TestCase): ('gdebi-test10.deb', False), ] + def setUp(self): + apt_pkg.config.set("APT::Architecture","i386") + apt_pkg.config.set("Dir::State::status", + "./test_debs/var/lib/dpkg/status") + self.cache = apt.Cache() + def testDebFile(self): - deb = apt.debfile.DebPackage() + deb = apt.debfile.DebPackage(cache=self.cache) for (filename, expected_res) in self.TEST_DEBS: logging.debug("testing %s, expecting %s" % (filename, expected_res)) deb.open(os.path.join("test_debs", filename)) res = deb.check() - self.assertEqual(res, expected_res) + self.assertEqual(res, expected_res, + "Unexpected result for package '%s' (got %s wanted %s)" % ( + filename, res, expected_res)) if __name__ == "__main__": #logging.basicConfig(level=logging.DEBUG) -- cgit v1.2.3