From 945b06bc69749ae0543a2e27b46bf9d1b8b9353a Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Thu, 14 Aug 2008 22:00:38 +0200 Subject: Enhance the package description by using line breaks only for abstracts and replacing the ASCII art bullets by an unicode dot. This was implemented in several applications e.g. gnome-app-install, update-manager or packagekit. As a side effect it breaks the behavior of those. --- apt/package.py | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/apt/package.py b/apt/package.py index 096b1bd1..f1ea02c5 100644 --- a/apt/package.py +++ b/apt/package.py @@ -19,11 +19,13 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA -import apt_pkg import sys import random +import re import string +import apt_pkg + #from gettext import gettext as _ import gettext def _(s): return gettext.dgettext("python-apt", s) @@ -235,7 +237,9 @@ class Package(object): summary = property(summary) def description(self, format=True): - """ Return the formated long description """ + """ + Return the formated long description + """ if not self._lookupRecord(): return "" # get the translated description @@ -248,12 +252,23 @@ class Package(object): except UnicodeDecodeError,e: s = _("Invalid unicode in description for '%s' (%s). " "Please report.") % (self.name,e) - for line in string.split(s,"\n"): - tmp = string.strip(line) - if tmp == ".": - desc += "\n" - else: - desc += tmp + "\n" + lines = string.split(s, "\n") + for i in range(len(lines)): + # Skip the first line, since its a duplication of the summary + if i == 0: continue + line = lines[i].strip() + # Replace all empty lines by line breaks + if line == ".": + desc += "\n" + continue + # Use dots for lists + p = re.compile(r'^(\s|\t)*(\*|0|-)',re.MULTILINE) + line = p.sub(ur'\n\u2022', line) + # Use line breaks only for abstracts + if desc == "" or desc[-1] == "\n": + desc += line + else: + desc += " " + line return desc description = property(description) -- cgit v1.2.3 From c06893544e6c2b4b1b3f6fb50e50ead6874a157f Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 15 Aug 2008 12:31:29 +0200 Subject: Add Package.getChangelog(): returns the changelog as unicode --- apt/package.py | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/apt/package.py b/apt/package.py index f1ea02c5..b664769d 100644 --- a/apt/package.py +++ b/apt/package.py @@ -19,13 +19,19 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA +import httplib import sys import random import re +import socket import string +import urllib2 import apt_pkg +# Set a timeout for the changelog download +socket.setdefaulttimeout(2) + #from gettext import gettext as _ import gettext def _(s): return gettext.dgettext("python-apt", s) @@ -70,6 +76,7 @@ class Package(object): self._pkg = pkgiter self._list = sourcelist # sourcelist self._pcache = pcache # python cache in cache.py + self._changelog = "" # Cached changelog pass # helper @@ -367,6 +374,141 @@ class Package(object): return 0 return ver.InstalledSize installedSize = property(installedSize) + + def getChangelog(self, uri=None, cancel_lock=None): + """ + Download the changelog of the package and return it as unicode + string + + uri: Is the uri to the changelog file. The following named variables + will be substituted: src_section, prefix, src_pkg and src_ver + For example the Ubuntu changelog: + uri = "http://changelogs.ubuntu.com/changelogs/pool" \\ + "/%(src_section)s/%(prefix)s/%(src_pkg)s" \\ + "/%(src_pkg)s_%(src_ver)s/changelog" + cancel_lock: If this threading.Lock() is set, the download will be + canceled + """ + # Return a cached changelog if available + if self._changelog != "": + return self._changelog + + if uri == None: + if self.candidateOrigin[0].origin == "Debian": + uri = "http://packages.debian.org/changelogs/pool" \ + "/%(src_section)s/%(prefix)s/%(src_pkg)s" \ + "/%(src_pkg)s_%(src_ver)s/changelog" + elif self.candidateOrigin[0].origin == "Ubuntu": + uri = "http://changelogs.ubuntu.com/changelogs/pool" \ + "/%(src_section)s/%(prefix)s/%(src_pkg)s" \ + "/%(src_pkg)s_%(src_ver)s/changelog" + else: + return _("The list of changes is not available") + + # get the src package name + src_pkg = self.sourcePackageName + + # assume "main" section + src_section = "main" + # use the section of the candidate as a starting point + section = self._depcache.GetCandidateVer(self._pkg).Section + + # get the source version, start with the binaries version + bin_ver = self.candidateVersion + src_ver = self.candidateVersion + #print "bin: %s" % binver + try: + # try to get the source version of the pkg, this differs + # for some (e.g. libnspr4 on ubuntu) + # this feature only works if the correct deb-src are in the + # sources.list + # otherwise we fall back to the binary version number + src_records = apt_pkg.GetPkgSrcRecords() + src_rec = src_records.Lookup(src_pkg) + if src_rec: + src_ver = src_records.Version + #if apt_pkg.VersionCompare(binver, srcver) > 0: + # srcver = binver + if not src_ver: + src_ver = bin_ver + #print "srcver: %s" % src_ver + section = src_records.Section + #print "srcsect: %s" % section + else: + # fail into the error handler + raise SystemError + except SystemError, e: + src_ver = bin_ver + + l = section.split("/") + if len(l) > 1: + src_section = l[0] + + # lib is handled special + prefix = src_pkg[0] + if src_pkg.startswith("lib"): + prefix = "lib" + src_pkg[3] + + # stip epoch + l = string.split(src_ver,":") + if len(l) > 1: + src_ver = "".join(l[1:]) + + uri = uri % {"src_section" : src_section, + "prefix" : prefix, + "src_pkg" : src_pkg, + "src_ver" : src_ver} + try: + # Check if the download was canceled + if cancel_lock and cancel_lock.isSet(): return "" + changelog_file = urllib2.urlopen(uri) + # do only get the lines that are new + changelog = "" + regexp = "^%s \((.*)\)(.*)$" % (re.escape(src_pkg)) + + i=0 + while True: + # Check if the download was canceled + if cancel_lock and cancel_lock.isSet(): return "" + # Read changelog line by line + line_raw = changelog_file.readline() + if line_raw == "": + break + # The changelog is encoded in utf-8, but since there isn't any + # http header, urllib2 seems to treat it as ascii + line = line_raw.decode("utf-8") + + #print line.encode('utf-8') + match = re.match(regexp, line) + if match: + # strip epoch from installed version + # and from changelog too + installed = self.installedVersion + if installed and ":" in installed: + installed = installed.split(":",1)[1] + changelog_ver = match.group(1) + if changelog_ver and ":" in changelog_ver: + changelog_ver = changelog_ver.split(":", 1)[1] + if installed and \ + apt_pkg.VersionCompare(changelog_ver, installed) <= 0: + break + # EOF (shouldn't really happen) + changelog += line + + # Print an error if we failed to extract a changelog + if len(changelog) == 0: + changelog = _("The list of changes is not available") + self._changelog = changelog + except urllib2.HTTPError,e: + return _("The list of changes is not available yet.\n\n" + "Please use http://launchpad.net/ubuntu/+source/%s/%s/" + "+changelog\n" + "until the changes become available or try again " + "later.") % (srcpkg, srcver), + except IOError, httplib.BadStatusLine: + return _("Failed to download the list of changes. \nPlease " + "check your Internet connection.") + return self._changelog # canidate origin class Origin: -- cgit v1.2.3 From 0b0b062b72b721e5f5b3387b6c0fb4de6f6a4e61 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Wed, 20 Aug 2008 10:57:44 +0200 Subject: Add the installedFiles property to the Package class --- apt/package.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/apt/package.py b/apt/package.py index b664769d..3b26fb2b 100644 --- a/apt/package.py +++ b/apt/package.py @@ -374,7 +374,21 @@ class Package(object): return 0 return ver.InstalledSize installedSize = property(installedSize) - + + def installedFiles(self): + """ Return the list of files installed on the system by the package """ + if not self.isInstalled: + return None + path = "/var/lib/dpkg/info/%s.list" % self.name + try: + list = open(path) + files = list.read().split("\n") + list.close() + except: + return None + return files + installedFiles = property(installedFiles) + def getChangelog(self, uri=None, cancel_lock=None): """ Download the changelog of the package and return it as unicode -- cgit v1.2.3 From 1c521e44441ae442edea8b9a8431273ae530daea Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Thu, 21 Aug 2008 05:41:45 +0200 Subject: Copy DebPackage and DscSrcPackage from GDebi into dpkg --- apt/dpkg.py | 479 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 479 insertions(+) create mode 100755 apt/dpkg.py diff --git a/apt/dpkg.py b/apt/dpkg.py new file mode 100755 index 00000000..1b433bf2 --- /dev/null +++ b/apt/dpkg.py @@ -0,0 +1,479 @@ +# Copyright (c) 2005-2007 Canonical +# +# AUTHOR: +# Michael Vogt +# +# This file is part of GDebi +# +# GDebi 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. +# +# GDebi 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 GDebi; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import warnings +from warnings import warn +warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning) +import apt_inst, apt_pkg +import apt +import sys +import os +from gettext import gettext as _ +from Cache import Cache + +class DebPackage(object): + debug = 0 + + def __init__(self, cache, file=None): + cache.clear() + self._cache = cache + self.file = file + self._needPkgs = [] + self._sections = {} + self._installedConflicts = set() + self._failureString = "" + if file != None: + self.open(file) + + def open(self, file): + """ read a deb """ + control = apt_inst.debExtractControl(open(file)) + self._sections = apt_pkg.ParseSection(control) + self.pkgName = self._sections["Package"] + + def _isOrGroupSatisfied(self, or_group): + """ this function gets a 'or_group' and analyzes if + at least one dependency of this group is already satisfied """ + self._dbg(2,"_checkOrGroup(): %s " % (or_group)) + + for dep in or_group: + depname = dep[0] + ver = dep[1] + oper = dep[2] + + # check for virtual pkgs + if not self._cache.has_key(depname): + if self._cache.isVirtualPkg(depname): + self._dbg(3,"_isOrGroupSatisfied(): %s is virtual dep" % depname) + for pkg in self._cache.getProvidersForVirtual(depname): + if pkg.isInstalled: + return True + continue + + inst = self._cache[depname] + instver = inst.installedVersion + if instver != None and apt_pkg.CheckDep(instver,oper,ver) == True: + return True + return False + + + def _satisfyOrGroup(self, or_group): + """ try to satisfy the or_group """ + + or_found = False + virtual_pkg = None + + for dep in or_group: + depname = dep[0] + ver = dep[1] + oper = dep[2] + + # if we don't have it in the cache, it may be virtual + if not self._cache.has_key(depname): + if not self._cache.isVirtualPkg(depname): + continue + providers = self._cache.getProvidersForVirtual(depname) + # if a package just has a single virtual provider, we + # just pick that (just like apt) + if len(providers) != 1: + continue + depname = providers[0].name + + # now check if we can satisfy the deps with the candidate(s) + # in the cache + cand = self._cache[depname] + candver = self._cache._depcache.GetCandidateVer(cand._pkg) + if not candver: + continue + if not apt_pkg.CheckDep(candver.VerStr,oper,ver): + continue + + # check if we need to install it + self._dbg(2,"Need to get: %s" % depname) + self._needPkgs.append(depname) + return True + + # if we reach this point, we failed + or_str = "" + for dep in or_group: + or_str += dep[0] + if dep != or_group[len(or_group)-1]: + or_str += "|" + self._failureString += _("Dependency is not satisfiable: %s\n" % or_str) + return False + + def _checkSinglePkgConflict(self, pkgname, ver, oper): + """ returns 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)) + pkgver = None + cand = self._cache[pkgname] + if cand.isInstalled: + pkgver = cand.installedVersion + elif cand.markedInstall: + pkgver = cand.candidateVersion + #print "pkg: %s" % pkgname + #print "ver: %s" % ver + #print "pkgver: %s " % pkgver + #print "oper: %s " % oper + if (pkgver and apt_pkg.CheckDep(pkgver,oper,ver) and + not self.replacesRealPkg(pkgname, oper, ver)): + self._failureString += _("Conflicts with the installed package '%s'" % cand.name) + return True + return False + + def _checkConflictsOrGroup(self, or_group): + """ check the or-group for conflicts with installed pkgs """ + self._dbg(2,"_checkConflictsOrGroup(): %s " % (or_group)) + + or_found = False + virtual_pkg = None + + for dep in or_group: + depname = dep[0] + ver = dep[1] + oper = dep[2] + + # check conflicts with virtual pkgs + if not self._cache.has_key(depname): + # FIXME: we have to check for virtual replaces here as + # well (to pass tests/gdebi-test8.deb) + if self._cache.isVirtualPkg(depname): + for pkg in self._cache.getProvidersForVirtual(depname): + self._dbg(3, "conflicts virtual check: %s" % pkg.name) + # P/C/R on virtal pkg, e.g. ftpd + if self.pkgName == pkg.name: + self._dbg(3, "conflict on self, ignoring") + continue + if self._checkSinglePkgConflict(pkg.name,ver,oper): + self._installedConflicts.add(pkg.name) + continue + if self._checkSinglePkgConflict(depname,ver,oper): + self._installedConflicts.add(depname) + return len(self._installedConflicts) != 0 + + def getConflicts(self): + conflicts = [] + key = "Conflicts" + if self._sections.has_key(key): + conflicts = apt_pkg.ParseDepends(self._sections[key]) + return conflicts + + def getDepends(self): + depends = [] + # find depends + for key in ["Depends","PreDepends"]: + if self._sections.has_key(key): + depends.extend(apt_pkg.ParseDepends(self._sections[key])) + return depends + + def getProvides(self): + provides = [] + key = "Provides" + if self._sections.has_key(key): + provides = apt_pkg.ParseDepends(self._sections[key]) + return provides + + def getReplaces(self): + replaces = [] + key = "Replaces" + if self._sections.has_key(key): + replaces = apt_pkg.ParseDepends(self._sections[key]) + return replaces + + def replacesRealPkg(self, pkgname, oper, ver): + """ + 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)) + pkgver = None + cand = self._cache[pkgname] + if cand.isInstalled: + pkgver = cand.installedVersion + elif cand.markedInstall: + pkgver = cand.candidateVersion + for or_group in self.getReplaces(): + for (name, ver, oper) in or_group: + if (name == pkgname and + apt_pkg.CheckDep(pkgver,oper,ver)): + self._dbg(3, "we have a replaces in our package for the conflict against '%s'" % (pkgname)) + return True + return False + + def checkConflicts(self): + """ check if the pkg conflicts with a existing or to be installed + package. Return True if the pkg is ok """ + res = True + for or_group in self.getConflicts(): + if self._checkConflictsOrGroup(or_group): + #print "Conflicts with a exisiting pkg!" + #self._failureString = "Conflicts with a exisiting pkg!" + res = False + return res + + # some constants + (NO_VERSION, + VERSION_OUTDATED, + VERSION_SAME, + VERSION_IS_NEWER) = range(4) + + def compareToVersionInCache(self, useInstalled=True): + """ checks if the pkg is already installed or availabe in the cache + and if so in what version, returns if the version of the deb + is not available,older,same,newer + """ + self._dbg(3,"compareToVersionInCache") + pkgname = self._sections["Package"] + debver = self._sections["Version"] + self._dbg(1,"debver: %s" % debver) + if self._cache.has_key(pkgname): + if useInstalled: + cachever = self._cache[pkgname].installedVersion + else: + cachever = self._cache[pkgname].candidateVersion + if cachever != None: + cmp = apt_pkg.VersionCompare(cachever,debver) + self._dbg(1, "CompareVersion(debver,instver): %s" % cmp) + if cmp == 0: + return self.VERSION_SAME + elif cmp < 0: + return self.VERSION_IS_NEWER + elif cmp > 0: + return self.VERSION_OUTDATED + return self.NO_VERSION + + def checkDeb(self): + self._dbg(3,"checkDepends") + + # check arch + arch = self._sections["Architecture"] + if arch != "all" and arch != apt_pkg.Config.Find("APT::Architecture"): + self._dbg(1,"ERROR: Wrong architecture dude!") + self._failureString = _("Wrong architecture '%s'" % arch) + return False + + # check version + res = self.compareToVersionInCache() + if res == self.VERSION_OUTDATED: # the deb is older than the installed + self._failureString = _("A later version is already installed") + return False + + # FIXME: this sort of error handling sux + self._failureString = "" + + # check conflicts + if not self.checkConflicts(): + return False + + # try to satisfy the dependencies + res = self._satisfyDepends(self.getDepends()) + if not res: + return False + + # check for conflicts again (this time with the packages that are + # makeed for install) + if not self.checkConflicts(): + return False + + if self._cache._depcache.BrokenCount > 0: + self._failureString = _("Failed to satisfy all dependencies (broken cache)") + # clean the cache again + self._cache.clear() + return False + return True + + def satisfyDependsStr(self, dependsstr): + return self._satisfyDepends(apt_pkg.ParseDepends(dependsstr)) + + def _satisfyDepends(self, depends): + # turn off MarkAndSweep via a action group (if available) + try: + _actiongroup = apt_pkg.GetPkgActionGroup(self._cache._depcache) + except AttributeError, e: + pass + # check depends + for or_group in depends: + #print "or_group: %s" % or_group + #print "or_group satified: %s" % self._isOrGroupSatisfied(or_group) + if not self._isOrGroupSatisfied(or_group): + if not self._satisfyOrGroup(or_group): + return False + # now try it out in the cache + for pkg in self._needPkgs: + try: + self._cache[pkg].markInstall(fromUser=False) + except SystemError, e: + self._failureString = _("Cannot install '%s'" % pkg) + self._cache.clear() + return False + return True + + def missingDeps(self): + self._dbg(1, "Installing: %s" % self._needPkgs) + if self._needPkgs == None: + self.checkDeb() + return self._needPkgs + missingDeps = property(missingDeps) + + def requiredChanges(self): + """ gets the required changes to satisfy the depends. + returns a tuple with (install, remove, unauthenticated) + """ + install = [] + remove = [] + unauthenticated = [] + for pkg in self._cache: + if pkg.markedInstall or pkg.markedUpgrade: + install.append(pkg.name) + # check authentication, one authenticated origin is enough + # libapt will skip non-authenticated origins then + authenticated = False + for origin in pkg.candidateOrigin: + authenticated |= origin.trusted + if not authenticated: + unauthenticated.append(pkg.name) + if pkg.markedDelete: + remove.append(pkg.name) + return (install,remove, unauthenticated) + requiredChanges = property(requiredChanges) + + 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) + 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 + filelist = property(filelist) + + # properties + def __getitem__(self,item): + if not self._sections.has_key(item): + # 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: + print >> sys.stderr, msg + + +class DscSrcPackage(DebPackage): + def __init__(self, cache, file=None): + DebPackage.__init__(self, cache) + self.file = file + self.depends = [] + self.conflicts = [] + self.binaries = [] + if file != None: + self.open(file) + def getConflicts(self): + return self.conflicts + def getDepends(self): + return self.depends + def open(self, file): + depends_tags = ["Build-Depends:", "Build-Depends-Indep:"] + conflicts_tags = ["Build-Conflicts:", "Build-Conflicts-Indep:"] + for line in open(file): + # check b-d and b-c + for tag in depends_tags: + if line.startswith(tag): + key = line[len(tag):].strip() + self.depends.extend(apt_pkg.ParseSrcDepends(key)) + for tag in conflicts_tags: + if line.startswith(tag): + key = line[len(tag):].strip() + self.conflicts.extend(apt_pkg.ParseSrcDepends(key)) + # check binary and source and version + if line.startswith("Source:"): + self.pkgName = line[len("Source:"):].strip() + if line.startswith("Binary:"): + self.binaries = [pkg.strip() for pkg in line[len("Binary:"):].split(",")] + if line.startswith("Version:"): + self._sections["Version"] = line[len("Version:"):].strip() + # we are at the end + if line.startswith("-----BEGIN PGP SIGNATURE-"): + break + s = _("Install Build-Dependencies for " + "source package '%s' that builds %s\n" + ) % (self.pkgName, " ".join(self.binaries)) + self._sections["Description"] = s + + def checkDeb(self): + if not self.checkConflicts(): + for pkgname in self._installedConflicts: + if self._cache[pkgname]._pkg.Essential: + raise Exception, _("A essential package would be removed") + self._cache[pkgname].markDelete() + # FIXME: a additional run of the checkConflicts() + # after _satisfyDepends() should probably be done + return self._satisfyDepends(self.depends) + +if __name__ == "__main__": + + cache = Cache() + + vp = "www-browser" + print "%s virtual: %s" % (vp,cache.isVirtualPkg(vp)) + providers = cache.getProvidersForVirtual(vp) + print "Providers for %s :" % vp + for pkg in providers: + print " %s" % pkg.name + + d = DebPackage(cache, sys.argv[1]) + print "Deb: %s" % d.pkgName + if not d.checkDeb(): + print "can't be satified" + print d._failureString + print "missing deps: %s" % d.missingDeps + print d.requiredChanges + + #s = DscSrcPackage(cache, "../tests/3ddesktop_0.2.9-6.dsc") + #s.checkDep() + #print "Missing deps: ",s.missingDeps + #print "Print required changes: ", s.requiredChanges + + s = DscSrcPackage(cache) + d = "libc6 (>= 2.3.2), libaio (>= 0.3.96) | libaio1 (>= 0.3.96)" + print s._satisfyDepends(apt_pkg.ParseDepends(d)) + -- cgit v1.2.3 From 1db0459ae3a8a2240ffb9585bb1c378d7b17acbb Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Thu, 21 Aug 2008 15:03:30 +0200 Subject: Add apt.Cache.clear() method (taken from GDebi), which re initializes the dependecy cache. --- apt/cache.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apt/cache.py b/apt/cache.py index bbf2165b..3df2b4a6 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -232,6 +232,10 @@ class Cache(object): fetcher.Shutdown() return (res == pm.ResultCompleted) + def clear(self): + """ Unmark all changes """ + self._depcache.Init() + # cache changes def cachePostChange(self): " called internally if the cache has changed, emit a signal then " -- cgit v1.2.3 From 312c6465f3ac7da68c610a01b4856cb45ae62b34 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Thu, 21 Aug 2008 18:34:41 +0200 Subject: Add apt.cache.Cache.isVirtualPackage() and apt.cache.Cache.getProvidingPackages() - taken from GDebi. --- apt/cache.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/apt/cache.py b/apt/cache.py index 3df2b4a6..e59e30c3 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -182,6 +182,51 @@ class Cache(object): finally: os.close(lock) + def isVirtualPackage(self, name): + """ + Return True if the package of the given name is a virtual package + """ + try: + virtual_pkg = self._cache[name] + except KeyError: + return False + if len(virtual_pkg.VersionList) == 0: + return True + return False + + def getProvidingPackages(self, virtual): + """ + Return a list of packages which provide the virtual package of the + specified name + """ + providers = [] + try: + vp = self._cache[virtual] + if len(vp.VersionList) != 0: + return providers + except IndexError: + return providers + for pkg in self: + v = self._depcache.GetCandidateVer(pkg._pkg) + if v == None: + continue + for p in v.ProvidesList: + #print virtual + #print p[0] + if virtual == 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 + def update(self, fetchProgress=None): " run the equivalent of apt-get update " lockfile = apt_pkg.Config.FindDir("Dir::State::Lists") + "lock" -- cgit v1.2.3 From fcb99cb79276d87ec47dedd7be3b0eefd278a0df Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Thu, 21 Aug 2008 18:36:31 +0200 Subject: Small fix: catch dict exceptions in getProvidingPackages --- apt/cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apt/cache.py b/apt/cache.py index e59e30c3..3fd1e996 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -204,7 +204,7 @@ class Cache(object): vp = self._cache[virtual] if len(vp.VersionList) != 0: return providers - except IndexError: + except KeyError: return providers for pkg in self: v = self._depcache.GetCandidateVer(pkg._pkg) -- cgit v1.2.3 From ee9a44068f1af026b2ce3f70f65c2090594eb2a3 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Thu, 21 Aug 2008 18:37:59 +0200 Subject: Make dpkg.py not executable --- apt/dpkg.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 apt/dpkg.py diff --git a/apt/dpkg.py b/apt/dpkg.py old mode 100755 new mode 100644 -- cgit v1.2.3 From 1fd97df89aabf0034a13c65251cbb1edc04867b5 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Thu, 21 Aug 2008 18:42:02 +0200 Subject: Adapt dpkg to use the Cache class of python-apt --- apt/dpkg.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apt/dpkg.py b/apt/dpkg.py index 1b433bf2..7f4341f3 100644 --- a/apt/dpkg.py +++ b/apt/dpkg.py @@ -28,7 +28,7 @@ import apt import sys import os from gettext import gettext as _ -from Cache import Cache +from cache import Cache class DebPackage(object): debug = 0 @@ -62,9 +62,9 @@ class DebPackage(object): # check for virtual pkgs if not self._cache.has_key(depname): - if self._cache.isVirtualPkg(depname): + if self._cache.isVirtualPackage(depname): self._dbg(3,"_isOrGroupSatisfied(): %s is virtual dep" % depname) - for pkg in self._cache.getProvidersForVirtual(depname): + for pkg in self._cache.getProvidingPackages(depname): if pkg.isInstalled: return True continue @@ -89,9 +89,9 @@ class DebPackage(object): # if we don't have it in the cache, it may be virtual if not self._cache.has_key(depname): - if not self._cache.isVirtualPkg(depname): + if not self._cache.isVirtualPackage(depname): continue - providers = self._cache.getProvidersForVirtual(depname) + providers = self._cache.getProvidingPackages(depname) # if a package just has a single virtual provider, we # just pick that (just like apt) if len(providers) != 1: @@ -159,8 +159,8 @@ class DebPackage(object): if not self._cache.has_key(depname): # FIXME: we have to check for virtual replaces here as # well (to pass tests/gdebi-test8.deb) - if self._cache.isVirtualPkg(depname): - for pkg in self._cache.getProvidersForVirtual(depname): + if self._cache.isVirtualPackage(depname): + for pkg in self._cache.getProvidingPackages(depname): self._dbg(3, "conflicts virtual check: %s" % pkg.name) # P/C/R on virtal pkg, e.g. ftpd if self.pkgName == pkg.name: @@ -454,8 +454,8 @@ if __name__ == "__main__": cache = Cache() vp = "www-browser" - print "%s virtual: %s" % (vp,cache.isVirtualPkg(vp)) - providers = cache.getProvidersForVirtual(vp) + print "%s virtual: %s" % (vp,cache.isVirtualPackage(vp)) + providers = cache.getProvidingPackages(vp) print "Providers for %s :" % vp for pkg in providers: print " %s" % pkg.name -- cgit v1.2.3 From 142e015192e7c4f97664a08d50753073106a689f Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 22 Aug 2008 05:11:47 +0200 Subject: Add DpkgInstallProgress from the GTK GDebi interface to the progress package. --- apt/progress.py | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/apt/progress.py b/apt/progress.py index bb1bce35..ff877ff8 100644 --- a/apt/progress.py +++ b/apt/progress.py @@ -233,6 +233,89 @@ class CdromProgress: def changeCdrom(self): pass + +class DpkgInstallProgress(object): + def __init__(self, debfile, status, progress, term, expander): + self.debfile = debfile + self.status = status + self.progress = progress + self.term = term + self.term_expander = expander + self.time_last_update = time.time() + self.term_expander.set_expanded(False) + def commit(self): + def finish_dpkg(term, pid, status, lock): + " helper " + self.exitstatus = posix.WEXITSTATUS(status) + #print "dpkg finished %s %s" % (pid,status) + #print "exit status: %s" % self.exitstatus + #print "was signaled %s" % posix.WIFSIGNALED(status) + lock.release() + + # get a lock + lock = thread.allocate_lock() + lock.acquire() + + # ui + self.status.set_markup(""+_("Installing '%s'...") % \ + os.path.basename(self.debfile)+"") + self.progress.pulse() + self.progress.set_text("") + + # prepare reading the pipe + (readfd, writefd) = os.pipe() + fcntl.fcntl(readfd, fcntl.F_SETFL,os.O_NONBLOCK) + #print "fds (%i,%i)" % (readfd,writefd) + + # the command + cmd = "/usr/bin/dpkg" + argv = [cmd,"--status-fd", "%s"%writefd, "-i", self.debfile] + env = ["VTE_PTY_KEEP_FD=%s"% writefd, + "DEBIAN_FRONTEND=gnome", + "APT_LISTCHANGES_FRONTEND=gtk"] + #print cmd + #print argv + #print env + #print self.term + + # prepare for the fork + reaper = vte.reaper_get() + signal_id = reaper.connect("child-exited", finish_dpkg, lock) + pid = self.term.fork_command(command=cmd, argv=argv, envv=env) + read = "" + while lock.locked(): + while True: + try: + read += os.read(readfd,1) + except OSError, (errno,errstr): + # resource temporarly unavailable is ignored + if errno != 11: + print errstr + break + self.time_last_update = time.time() + if read.endswith("\n"): + statusl = string.split(read, ":") + if len(statusl) < 3: + print "got garbage from dpkg: '%s'" % read + read = "" + break + status = statusl[2].strip() + #print status + if status == "error" or status == "conffile-prompt": + self.term_expander.set_expanded(True) + read = "" + self.progress.pulse() + while gtk.events_pending(): + gtk.main_iteration() + time.sleep(0.2) + # if the terminal has not reacted for some time, do something + if (not self.term_expander.get_expanded() and + (self.time_last_update + GDEBI_TERMINAL_TIMEOUT) < time.time()): + self.term_expander.set_expanded(True) + self.progress.set_fraction(1.0) + reaper.disconnect(signal_id) + + # module test code if __name__ == "__main__": import apt_pkg -- cgit v1.2.3 From 5ea022079850f3d7ab2411cf3f150435f140fdfe Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 22 Aug 2008 06:11:16 +0200 Subject: Make DpkgInstallProgress an inheritance of InstallProgress --- apt/dpkg.py | 13 +++++++ apt/progress.py | 105 ++++++++++++++++++++------------------------------------ 2 files changed, 51 insertions(+), 67 deletions(-) diff --git a/apt/dpkg.py b/apt/dpkg.py index 7f4341f3..7ebc551f 100644 --- a/apt/dpkg.py +++ b/apt/dpkg.py @@ -29,6 +29,7 @@ import sys import os from gettext import gettext as _ from cache import Cache +from progress import DpkgInstallProgress class DebPackage(object): debug = 0 @@ -397,6 +398,13 @@ class DebPackage(object): if level <= self.debug: print >> sys.stderr, msg + def install(self, installProgress=None): + """ Install the package """ + if installProgress == None: + res = os.system("/usr/sbin/dpkg -i %s" % self.file) + else: + res = installProgress.run(self.file) + return res class DscSrcPackage(DebPackage): def __init__(self, cache, file=None): @@ -468,6 +476,10 @@ if __name__ == "__main__": print "missing deps: %s" % d.missingDeps print d.requiredChanges + print "Installing ..." + ret = d.install(DpkgInstallProgress()) + print ret + #s = DscSrcPackage(cache, "../tests/3ddesktop_0.2.9-6.dsc") #s.checkDep() #print "Missing deps: ",s.missingDeps @@ -477,3 +489,4 @@ if __name__ == "__main__": d = "libc6 (>= 2.3.2), libaio (>= 0.3.96) | libaio1 (>= 0.3.96)" print s._satisfyDepends(apt_pkg.ParseDepends(d)) + diff --git a/apt/progress.py b/apt/progress.py index ff877ff8..87ccb03b 100644 --- a/apt/progress.py +++ b/apt/progress.py @@ -234,87 +234,58 @@ class CdromProgress: pass -class DpkgInstallProgress(object): - def __init__(self, debfile, status, progress, term, expander): +class DpkgInstallProgress(InstallProgress): + """ + Progress handler for a local Debian package installation + """ + def run(self, debfile): + """ + Start installing the given Debian package + """ self.debfile = debfile - self.status = status - self.progress = progress - self.term = term - self.term_expander = expander - self.time_last_update = time.time() - self.term_expander.set_expanded(False) - def commit(self): - def finish_dpkg(term, pid, status, lock): - " helper " - self.exitstatus = posix.WEXITSTATUS(status) - #print "dpkg finished %s %s" % (pid,status) - #print "exit status: %s" % self.exitstatus - #print "was signaled %s" % posix.WIFSIGNALED(status) - lock.release() - - # get a lock - lock = thread.allocate_lock() - lock.acquire() - - # ui - self.status.set_markup(""+_("Installing '%s'...") % \ - os.path.basename(self.debfile)+"") - self.progress.pulse() - self.progress.set_text("") - - # prepare reading the pipe - (readfd, writefd) = os.pipe() - fcntl.fcntl(readfd, fcntl.F_SETFL,os.O_NONBLOCK) - #print "fds (%i,%i)" % (readfd,writefd) - - # the command - cmd = "/usr/bin/dpkg" - argv = [cmd,"--status-fd", "%s"%writefd, "-i", self.debfile] - env = ["VTE_PTY_KEEP_FD=%s"% writefd, - "DEBIAN_FRONTEND=gnome", - "APT_LISTCHANGES_FRONTEND=gtk"] - #print cmd - #print argv - #print env - #print self.term + self.debname = os.path.basename(debfile).split("_")[0] + pid = self.fork() + if pid == 0: + # child + res = os.system("/usr/bin/dpkg --status-fd %s -i %s" % \ + (self.writefd, self.debfile)) + os._exit(res) + self.child_pid = pid + res = self.waitChild() + return res - # prepare for the fork - reaper = vte.reaper_get() - signal_id = reaper.connect("child-exited", finish_dpkg, lock) - pid = self.term.fork_command(command=cmd, argv=argv, envv=env) - read = "" - while lock.locked(): + def updateInterface(self): + """ + Process status messages from dpkg + """ + if self.statusfd != None: while True: try: - read += os.read(readfd,1) + self.read += os.read(self.statusfd.fileno(),1) except OSError, (errno,errstr): # resource temporarly unavailable is ignored if errno != 11: print errstr break - self.time_last_update = time.time() - if read.endswith("\n"): - statusl = string.split(read, ":") + if self.read.endswith("\n"): + statusl = string.split(self.read, ":") if len(statusl) < 3: print "got garbage from dpkg: '%s'" % read - read = "" + self.read = "" break status = statusl[2].strip() #print status - if status == "error" or status == "conffile-prompt": - self.term_expander.set_expanded(True) - read = "" - self.progress.pulse() - while gtk.events_pending(): - gtk.main_iteration() - time.sleep(0.2) - # if the terminal has not reacted for some time, do something - if (not self.term_expander.get_expanded() and - (self.time_last_update + GDEBI_TERMINAL_TIMEOUT) < time.time()): - self.term_expander.set_expanded(True) - self.progress.set_fraction(1.0) - reaper.disconnect(signal_id) - + if status == "error": + self.error(self.debname, status) + elif status == "conffile-prompt": + # we get a string like this: + # 'current-conffile' 'new-conffile' useredited distedited + match = re.compile("\s*\'(.*)\'\s*\'(.*)\'.*").match(status_str) + if match: + self.conffile(match.group(1), match.group(2)) + else: + self.status = status + self.read = "" # module test code if __name__ == "__main__": -- cgit v1.2.3 From a39a102e437702e7cd1c3f314e507d6b0d466eb5 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 22 Aug 2008 06:11:52 +0200 Subject: Fix: os._exit does not send the exit status the main process --- apt/progress.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apt/progress.py b/apt/progress.py index 87ccb03b..2ef100a8 100644 --- a/apt/progress.py +++ b/apt/progress.py @@ -214,7 +214,7 @@ class InstallProgress(DumbInstallProgress): if pid == 0: # child res = pm.DoInstall(self.writefd) - os._exit(res) + os.exit(res) self.child_pid = pid res = self.waitChild() return res @@ -249,7 +249,7 @@ class DpkgInstallProgress(InstallProgress): # child res = os.system("/usr/bin/dpkg --status-fd %s -i %s" % \ (self.writefd, self.debfile)) - os._exit(res) + os.exit(res) self.child_pid = pid res = self.waitChild() return res -- cgit v1.2.3 From 0cc53f40247e2e31ad99b0933e08c17c22c57079 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Sat, 23 Aug 2008 22:13:04 +0200 Subject: Merge the Debfile class from dpkg into the one from the debfile package. --- apt/debfile.py | 455 ++++++++++++++++++++++++++++++++++++++++++++++++++-- apt/dpkg.py | 492 --------------------------------------------------------- 2 files changed, 441 insertions(+), 506 deletions(-) delete mode 100644 apt/dpkg.py diff --git a/apt/debfile.py b/apt/debfile.py index ddde5bf1..36ad2594 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -1,8 +1,35 @@ -import apt_inst -import apt_pkg -from apt_inst import arCheckMember +# Copyright (c) 2005-2007 Canonical +# +# AUTHOR: +# Michael Vogt +# +# This file is part of GDebi +# +# GDebi 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. +# +# GDebi 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 GDebi; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +import warnings +from warnings import warn +warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning) +import apt_inst, apt_pkg +import apt +import sys +import os from gettext import gettext as _ +from cache import Cache +from progress import DpkgInstallProgress class NoDebArchiveException(IOError): pass @@ -11,15 +38,22 @@ class DebPackage(object): _supported_data_members = ("data.tar.gz", "data.tar.bz2", "data.tar.lzma") - def __init__(self, filename=None): - self._section = {} + debug = 0 + + def __init__(self, filename=None, cache=None): + self._cache = cache + self.file = filename + self._needPkgs = [] + self._sections = {} + self._installedConflicts = set() + self._failureString = "" if filename: self.open(filename) def open(self, filename): " open given debfile " self.filename = filename - if not arCheckMember(open(self.filename), "debian-binary"): + if not apt_inst.arCheckMember(open(self.filename), "debian-binary"): raise NoDebArchiveException, _("This is not a valid DEB archive, missing '%s' member" % "debian-binary") control = apt_inst.debExtractControl(open(self.filename)) self._sections = apt_pkg.ParseSection(control) @@ -36,7 +70,7 @@ class DebPackage(object): # % (What,Name,Link,Mode,UID,GID,Size, MTime, Major, Minor) files.append(Name) for member in self._supported_data_members: - if arCheckMember(open(self.filename), member): + if apt_inst.arCheckMember(open(self.filename), member): try: apt_inst.debExtract(open(self.filename), extract_cb, member) break @@ -45,14 +79,407 @@ class DebPackage(object): return files filelist = property(filelist) + def _isOrGroupSatisfied(self, or_group): + """ this function gets a 'or_group' and analyzes if + at least one dependency of this group is already satisfied """ + self._dbg(2,"_checkOrGroup(): %s " % (or_group)) + for dep in or_group: + depname = dep[0] + ver = dep[1] + oper = dep[2] -if __name__ == "__main__": - import sys + # check for virtual pkgs + if not self._cache.has_key(depname): + if self._cache.isVirtualPackage(depname): + self._dbg(3,"_isOrGroupSatisfied(): %s is virtual dep" % depname) + for pkg in self._cache.getProvidingPackages(depname): + if pkg.isInstalled: + return True + continue + + inst = self._cache[depname] + instver = inst.installedVersion + if instver != None and apt_pkg.CheckDep(instver,oper,ver) == True: + return True + return False + + + def _satisfyOrGroup(self, or_group): + """ try to satisfy the or_group """ + + or_found = False + virtual_pkg = None + + for dep in or_group: + depname = dep[0] + ver = dep[1] + oper = dep[2] + + # if we don't have it in the cache, it may be virtual + if not self._cache.has_key(depname): + if not self._cache.isVirtualPackage(depname): + continue + providers = self._cache.getProvidingPackages(depname) + # if a package just has a single virtual provider, we + # just pick that (just like apt) + if len(providers) != 1: + continue + depname = providers[0].name + + # now check if we can satisfy the deps with the candidate(s) + # in the cache + cand = self._cache[depname] + candver = self._cache._depcache.GetCandidateVer(cand._pkg) + if not candver: + continue + if not apt_pkg.CheckDep(candver.VerStr,oper,ver): + continue + + # check if we need to install it + self._dbg(2,"Need to get: %s" % depname) + self._needPkgs.append(depname) + return True + + # if we reach this point, we failed + or_str = "" + for dep in or_group: + or_str += dep[0] + if dep != or_group[len(or_group)-1]: + or_str += "|" + self._failureString += _("Dependency is not satisfiable: %s\n" % or_str) + return False + + def _checkSinglePkgConflict(self, pkgname, ver, oper): + """ returns 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)) + pkgver = None + cand = self._cache[pkgname] + if cand.isInstalled: + pkgver = cand.installedVersion + elif cand.markedInstall: + pkgver = cand.candidateVersion + #print "pkg: %s" % pkgname + #print "ver: %s" % ver + #print "pkgver: %s " % pkgver + #print "oper: %s " % oper + if (pkgver and apt_pkg.CheckDep(pkgver,oper,ver) and + not self.replacesRealPkg(pkgname, oper, ver)): + self._failureString += _("Conflicts with the installed package '%s'" % cand.name) + return True + return False + + def _checkConflictsOrGroup(self, or_group): + """ check the or-group for conflicts with installed pkgs """ + self._dbg(2,"_checkConflictsOrGroup(): %s " % (or_group)) + + or_found = False + virtual_pkg = None + + for dep in or_group: + depname = dep[0] + ver = dep[1] + oper = dep[2] + + # check conflicts with virtual pkgs + if not self._cache.has_key(depname): + # FIXME: we have to check for virtual replaces here as + # well (to pass tests/gdebi-test8.deb) + if self._cache.isVirtualPackage(depname): + for pkg in self._cache.getProvidingPackages(depname): + self._dbg(3, "conflicts virtual check: %s" % pkg.name) + # P/C/R on virtal pkg, e.g. ftpd + if self.pkgName == pkg.name: + self._dbg(3, "conflict on self, ignoring") + continue + if self._checkSinglePkgConflict(pkg.name,ver,oper): + self._installedConflicts.add(pkg.name) + continue + if self._checkSinglePkgConflict(depname,ver,oper): + self._installedConflicts.add(depname) + return len(self._installedConflicts) != 0 + + def getConflicts(self): + conflicts = [] + key = "Conflicts" + if self._sections.has_key(key): + conflicts = apt_pkg.ParseDepends(self._sections[key]) + return conflicts + + def getDepends(self): + depends = [] + # find depends + for key in ["Depends","PreDepends"]: + if self._sections.has_key(key): + depends.extend(apt_pkg.ParseDepends(self._sections[key])) + return depends + + def getProvides(self): + provides = [] + key = "Provides" + if self._sections.has_key(key): + provides = apt_pkg.ParseDepends(self._sections[key]) + return provides + + def getReplaces(self): + replaces = [] + key = "Replaces" + if self._sections.has_key(key): + replaces = apt_pkg.ParseDepends(self._sections[key]) + return replaces + + def replacesRealPkg(self, pkgname, oper, ver): + """ + 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)) + pkgver = None + cand = self._cache[pkgname] + if cand.isInstalled: + pkgver = cand.installedVersion + elif cand.markedInstall: + pkgver = cand.candidateVersion + for or_group in self.getReplaces(): + for (name, ver, oper) in or_group: + if (name == pkgname and + apt_pkg.CheckDep(pkgver,oper,ver)): + self._dbg(3, "we have a replaces in our package for the conflict against '%s'" % (pkgname)) + return True + return False + + def checkConflicts(self): + """ check if the pkg conflicts with a existing or to be installed + package. Return True if the pkg is ok """ + res = True + for or_group in self.getConflicts(): + if self._checkConflictsOrGroup(or_group): + #print "Conflicts with a exisiting pkg!" + #self._failureString = "Conflicts with a exisiting pkg!" + res = False + return res + + # some constants + (NO_VERSION, + VERSION_OUTDATED, + VERSION_SAME, + VERSION_IS_NEWER) = range(4) - d = DebPackage(sys.argv[1]) - print d["Section"] - print d["Maintainer"] - print "Files:" - print "\n".join(d.filelist) + def compareToVersionInCache(self, useInstalled=True): + """ checks if the pkg is already installed or availabe in the cache + and if so in what version, returns if the version of the deb + is not available,older,same,newer + """ + self._dbg(3,"compareToVersionInCache") + pkgname = self._sections["Package"] + debver = self._sections["Version"] + self._dbg(1,"debver: %s" % debver) + if self._cache.has_key(pkgname): + if useInstalled: + cachever = self._cache[pkgname].installedVersion + else: + cachever = self._cache[pkgname].candidateVersion + if cachever != None: + cmp = apt_pkg.VersionCompare(cachever,debver) + self._dbg(1, "CompareVersion(debver,instver): %s" % cmp) + if cmp == 0: + return self.VERSION_SAME + elif cmp < 0: + return self.VERSION_IS_NEWER + elif cmp > 0: + return self.VERSION_OUTDATED + return self.NO_VERSION + + def checkDeb(self): + self._dbg(3,"checkDepends") + + # check arch + arch = self._sections["Architecture"] + if arch != "all" and arch != apt_pkg.Config.Find("APT::Architecture"): + self._dbg(1,"ERROR: Wrong architecture dude!") + self._failureString = _("Wrong architecture '%s'" % arch) + return False + + # check version + res = self.compareToVersionInCache() + if res == self.VERSION_OUTDATED: # the deb is older than the installed + self._failureString = _("A later version is already installed") + return False + + # FIXME: this sort of error handling sux + self._failureString = "" + + # check conflicts + if not self.checkConflicts(): + return False + + # try to satisfy the dependencies + res = self._satisfyDepends(self.getDepends()) + if not res: + return False + + # check for conflicts again (this time with the packages that are + # makeed for install) + if not self.checkConflicts(): + return False + + if self._cache._depcache.BrokenCount > 0: + self._failureString = _("Failed to satisfy all dependencies (broken cache)") + # clean the cache again + self._cache.clear() + return False + return True + + def satisfyDependsStr(self, dependsstr): + return self._satisfyDepends(apt_pkg.ParseDepends(dependsstr)) + + def _satisfyDepends(self, depends): + # turn off MarkAndSweep via a action group (if available) + try: + _actiongroup = apt_pkg.GetPkgActionGroup(self._cache._depcache) + except AttributeError, e: + pass + # check depends + for or_group in depends: + #print "or_group: %s" % or_group + #print "or_group satified: %s" % self._isOrGroupSatisfied(or_group) + if not self._isOrGroupSatisfied(or_group): + if not self._satisfyOrGroup(or_group): + return False + # now try it out in the cache + for pkg in self._needPkgs: + try: + self._cache[pkg].markInstall(fromUser=False) + except SystemError, e: + self._failureString = _("Cannot install '%s'" % pkg) + self._cache.clear() + return False + return True + + def missingDeps(self): + self._dbg(1, "Installing: %s" % self._needPkgs) + if self._needPkgs == None: + self.checkDeb() + return self._needPkgs + missingDeps = property(missingDeps) + + def requiredChanges(self): + """ gets the required changes to satisfy the depends. + returns a tuple with (install, remove, unauthenticated) + """ + install = [] + remove = [] + unauthenticated = [] + for pkg in self._cache: + if pkg.markedInstall or pkg.markedUpgrade: + install.append(pkg.name) + # check authentication, one authenticated origin is enough + # libapt will skip non-authenticated origins then + authenticated = False + for origin in pkg.candidateOrigin: + authenticated |= origin.trusted + if not authenticated: + unauthenticated.append(pkg.name) + if pkg.markedDelete: + remove.append(pkg.name) + return (install,remove, unauthenticated) + requiredChanges = property(requiredChanges) + + def _dbg(self, level, msg): + """Write debugging output to sys.stderr. + """ + if level <= self.debug: + print >> sys.stderr, msg + + def install(self, installProgress=None): + """ Install the package """ + if installProgress == None: + res = os.system("/usr/sbin/dpkg -i %s" % self.filename) + else: + res = installProgress.run(self.filename) + return res + +class DscSrcPackage(DebPackage): + def __init__(self, filename=None, cache=None): + DebPackage.__init__(self, filename, cache) + self.depends = [] + self.conflicts = [] + self.binaries = [] + if filename != None: + self.open(filename) + def getConflicts(self): + return self.conflicts + def getDepends(self): + return self.depends + def open(self, file): + depends_tags = ["Build-Depends:", "Build-Depends-Indep:"] + conflicts_tags = ["Build-Conflicts:", "Build-Conflicts-Indep:"] + for line in open(file): + # check b-d and b-c + for tag in depends_tags: + if line.startswith(tag): + key = line[len(tag):].strip() + self.depends.extend(apt_pkg.ParseSrcDepends(key)) + for tag in conflicts_tags: + if line.startswith(tag): + key = line[len(tag):].strip() + self.conflicts.extend(apt_pkg.ParseSrcDepends(key)) + # check binary and source and version + if line.startswith("Source:"): + self.pkgName = line[len("Source:"):].strip() + if line.startswith("Binary:"): + self.binaries = [pkg.strip() for pkg in line[len("Binary:"):].split(",")] + if line.startswith("Version:"): + self._sections["Version"] = line[len("Version:"):].strip() + # we are at the end + if line.startswith("-----BEGIN PGP SIGNATURE-"): + break + s = _("Install Build-Dependencies for " + "source package '%s' that builds %s\n" + ) % (self.pkgName, " ".join(self.binaries)) + self._sections["Description"] = s + + def checkDeb(self): + if not self.checkConflicts(): + for pkgname in self._installedConflicts: + if self._cache[pkgname]._pkg.Essential: + raise Exception, _("A essential package would be removed") + self._cache[pkgname].markDelete() + # FIXME: a additional run of the checkConflicts() + # after _satisfyDepends() should probably be done + return self._satisfyDepends(self.depends) + +if __name__ == "__main__": + + cache = Cache() + + vp = "www-browser" + print "%s virtual: %s" % (vp,cache.isVirtualPackage(vp)) + providers = cache.getProvidingPackages(vp) + print "Providers for %s :" % vp + for pkg in providers: + print " %s" % pkg.name + d = DebPackage(sys.argv[1], cache) + print "Deb: %s" % d.pkgname + if not d.checkDeb(): + print "can't be satified" + print d._failureString + print "missing deps: %s" % d.missingDeps + print d.requiredChanges + + print "Installing ..." + ret = d.install(DpkgInstallProgress()) + print ret + + #s = DscSrcPackage(cache, "../tests/3ddesktop_0.2.9-6.dsc") + #s.checkDep() + #print "Missing deps: ",s.missingDeps + #print "Print required changes: ", s.requiredChanges + + s = DscSrcPackage(cache=cache) + d = "libc6 (>= 2.3.2), libaio (>= 0.3.96) | libaio1 (>= 0.3.96)" + print s._satisfyDepends(apt_pkg.ParseDepends(d)) diff --git a/apt/dpkg.py b/apt/dpkg.py deleted file mode 100644 index 7ebc551f..00000000 --- a/apt/dpkg.py +++ /dev/null @@ -1,492 +0,0 @@ -# Copyright (c) 2005-2007 Canonical -# -# AUTHOR: -# Michael Vogt -# -# This file is part of GDebi -# -# GDebi 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. -# -# GDebi 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 GDebi; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# - -import warnings -from warnings import warn -warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning) -import apt_inst, apt_pkg -import apt -import sys -import os -from gettext import gettext as _ -from cache import Cache -from progress import DpkgInstallProgress - -class DebPackage(object): - debug = 0 - - def __init__(self, cache, file=None): - cache.clear() - self._cache = cache - self.file = file - self._needPkgs = [] - self._sections = {} - self._installedConflicts = set() - self._failureString = "" - if file != None: - self.open(file) - - def open(self, file): - """ read a deb """ - control = apt_inst.debExtractControl(open(file)) - self._sections = apt_pkg.ParseSection(control) - self.pkgName = self._sections["Package"] - - def _isOrGroupSatisfied(self, or_group): - """ this function gets a 'or_group' and analyzes if - at least one dependency of this group is already satisfied """ - self._dbg(2,"_checkOrGroup(): %s " % (or_group)) - - for dep in or_group: - depname = dep[0] - ver = dep[1] - oper = dep[2] - - # check for virtual pkgs - if not self._cache.has_key(depname): - if self._cache.isVirtualPackage(depname): - self._dbg(3,"_isOrGroupSatisfied(): %s is virtual dep" % depname) - for pkg in self._cache.getProvidingPackages(depname): - if pkg.isInstalled: - return True - continue - - inst = self._cache[depname] - instver = inst.installedVersion - if instver != None and apt_pkg.CheckDep(instver,oper,ver) == True: - return True - return False - - - def _satisfyOrGroup(self, or_group): - """ try to satisfy the or_group """ - - or_found = False - virtual_pkg = None - - for dep in or_group: - depname = dep[0] - ver = dep[1] - oper = dep[2] - - # if we don't have it in the cache, it may be virtual - if not self._cache.has_key(depname): - if not self._cache.isVirtualPackage(depname): - continue - providers = self._cache.getProvidingPackages(depname) - # if a package just has a single virtual provider, we - # just pick that (just like apt) - if len(providers) != 1: - continue - depname = providers[0].name - - # now check if we can satisfy the deps with the candidate(s) - # in the cache - cand = self._cache[depname] - candver = self._cache._depcache.GetCandidateVer(cand._pkg) - if not candver: - continue - if not apt_pkg.CheckDep(candver.VerStr,oper,ver): - continue - - # check if we need to install it - self._dbg(2,"Need to get: %s" % depname) - self._needPkgs.append(depname) - return True - - # if we reach this point, we failed - or_str = "" - for dep in or_group: - or_str += dep[0] - if dep != or_group[len(or_group)-1]: - or_str += "|" - self._failureString += _("Dependency is not satisfiable: %s\n" % or_str) - return False - - def _checkSinglePkgConflict(self, pkgname, ver, oper): - """ returns 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)) - pkgver = None - cand = self._cache[pkgname] - if cand.isInstalled: - pkgver = cand.installedVersion - elif cand.markedInstall: - pkgver = cand.candidateVersion - #print "pkg: %s" % pkgname - #print "ver: %s" % ver - #print "pkgver: %s " % pkgver - #print "oper: %s " % oper - if (pkgver and apt_pkg.CheckDep(pkgver,oper,ver) and - not self.replacesRealPkg(pkgname, oper, ver)): - self._failureString += _("Conflicts with the installed package '%s'" % cand.name) - return True - return False - - def _checkConflictsOrGroup(self, or_group): - """ check the or-group for conflicts with installed pkgs """ - self._dbg(2,"_checkConflictsOrGroup(): %s " % (or_group)) - - or_found = False - virtual_pkg = None - - for dep in or_group: - depname = dep[0] - ver = dep[1] - oper = dep[2] - - # check conflicts with virtual pkgs - if not self._cache.has_key(depname): - # FIXME: we have to check for virtual replaces here as - # well (to pass tests/gdebi-test8.deb) - if self._cache.isVirtualPackage(depname): - for pkg in self._cache.getProvidingPackages(depname): - self._dbg(3, "conflicts virtual check: %s" % pkg.name) - # P/C/R on virtal pkg, e.g. ftpd - if self.pkgName == pkg.name: - self._dbg(3, "conflict on self, ignoring") - continue - if self._checkSinglePkgConflict(pkg.name,ver,oper): - self._installedConflicts.add(pkg.name) - continue - if self._checkSinglePkgConflict(depname,ver,oper): - self._installedConflicts.add(depname) - return len(self._installedConflicts) != 0 - - def getConflicts(self): - conflicts = [] - key = "Conflicts" - if self._sections.has_key(key): - conflicts = apt_pkg.ParseDepends(self._sections[key]) - return conflicts - - def getDepends(self): - depends = [] - # find depends - for key in ["Depends","PreDepends"]: - if self._sections.has_key(key): - depends.extend(apt_pkg.ParseDepends(self._sections[key])) - return depends - - def getProvides(self): - provides = [] - key = "Provides" - if self._sections.has_key(key): - provides = apt_pkg.ParseDepends(self._sections[key]) - return provides - - def getReplaces(self): - replaces = [] - key = "Replaces" - if self._sections.has_key(key): - replaces = apt_pkg.ParseDepends(self._sections[key]) - return replaces - - def replacesRealPkg(self, pkgname, oper, ver): - """ - 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)) - pkgver = None - cand = self._cache[pkgname] - if cand.isInstalled: - pkgver = cand.installedVersion - elif cand.markedInstall: - pkgver = cand.candidateVersion - for or_group in self.getReplaces(): - for (name, ver, oper) in or_group: - if (name == pkgname and - apt_pkg.CheckDep(pkgver,oper,ver)): - self._dbg(3, "we have a replaces in our package for the conflict against '%s'" % (pkgname)) - return True - return False - - def checkConflicts(self): - """ check if the pkg conflicts with a existing or to be installed - package. Return True if the pkg is ok """ - res = True - for or_group in self.getConflicts(): - if self._checkConflictsOrGroup(or_group): - #print "Conflicts with a exisiting pkg!" - #self._failureString = "Conflicts with a exisiting pkg!" - res = False - return res - - # some constants - (NO_VERSION, - VERSION_OUTDATED, - VERSION_SAME, - VERSION_IS_NEWER) = range(4) - - def compareToVersionInCache(self, useInstalled=True): - """ checks if the pkg is already installed or availabe in the cache - and if so in what version, returns if the version of the deb - is not available,older,same,newer - """ - self._dbg(3,"compareToVersionInCache") - pkgname = self._sections["Package"] - debver = self._sections["Version"] - self._dbg(1,"debver: %s" % debver) - if self._cache.has_key(pkgname): - if useInstalled: - cachever = self._cache[pkgname].installedVersion - else: - cachever = self._cache[pkgname].candidateVersion - if cachever != None: - cmp = apt_pkg.VersionCompare(cachever,debver) - self._dbg(1, "CompareVersion(debver,instver): %s" % cmp) - if cmp == 0: - return self.VERSION_SAME - elif cmp < 0: - return self.VERSION_IS_NEWER - elif cmp > 0: - return self.VERSION_OUTDATED - return self.NO_VERSION - - def checkDeb(self): - self._dbg(3,"checkDepends") - - # check arch - arch = self._sections["Architecture"] - if arch != "all" and arch != apt_pkg.Config.Find("APT::Architecture"): - self._dbg(1,"ERROR: Wrong architecture dude!") - self._failureString = _("Wrong architecture '%s'" % arch) - return False - - # check version - res = self.compareToVersionInCache() - if res == self.VERSION_OUTDATED: # the deb is older than the installed - self._failureString = _("A later version is already installed") - return False - - # FIXME: this sort of error handling sux - self._failureString = "" - - # check conflicts - if not self.checkConflicts(): - return False - - # try to satisfy the dependencies - res = self._satisfyDepends(self.getDepends()) - if not res: - return False - - # check for conflicts again (this time with the packages that are - # makeed for install) - if not self.checkConflicts(): - return False - - if self._cache._depcache.BrokenCount > 0: - self._failureString = _("Failed to satisfy all dependencies (broken cache)") - # clean the cache again - self._cache.clear() - return False - return True - - def satisfyDependsStr(self, dependsstr): - return self._satisfyDepends(apt_pkg.ParseDepends(dependsstr)) - - def _satisfyDepends(self, depends): - # turn off MarkAndSweep via a action group (if available) - try: - _actiongroup = apt_pkg.GetPkgActionGroup(self._cache._depcache) - except AttributeError, e: - pass - # check depends - for or_group in depends: - #print "or_group: %s" % or_group - #print "or_group satified: %s" % self._isOrGroupSatisfied(or_group) - if not self._isOrGroupSatisfied(or_group): - if not self._satisfyOrGroup(or_group): - return False - # now try it out in the cache - for pkg in self._needPkgs: - try: - self._cache[pkg].markInstall(fromUser=False) - except SystemError, e: - self._failureString = _("Cannot install '%s'" % pkg) - self._cache.clear() - return False - return True - - def missingDeps(self): - self._dbg(1, "Installing: %s" % self._needPkgs) - if self._needPkgs == None: - self.checkDeb() - return self._needPkgs - missingDeps = property(missingDeps) - - def requiredChanges(self): - """ gets the required changes to satisfy the depends. - returns a tuple with (install, remove, unauthenticated) - """ - install = [] - remove = [] - unauthenticated = [] - for pkg in self._cache: - if pkg.markedInstall or pkg.markedUpgrade: - install.append(pkg.name) - # check authentication, one authenticated origin is enough - # libapt will skip non-authenticated origins then - authenticated = False - for origin in pkg.candidateOrigin: - authenticated |= origin.trusted - if not authenticated: - unauthenticated.append(pkg.name) - if pkg.markedDelete: - remove.append(pkg.name) - return (install,remove, unauthenticated) - requiredChanges = property(requiredChanges) - - 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) - 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 - filelist = property(filelist) - - # properties - def __getitem__(self,item): - if not self._sections.has_key(item): - # 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: - print >> sys.stderr, msg - - def install(self, installProgress=None): - """ Install the package """ - if installProgress == None: - res = os.system("/usr/sbin/dpkg -i %s" % self.file) - else: - res = installProgress.run(self.file) - return res - -class DscSrcPackage(DebPackage): - def __init__(self, cache, file=None): - DebPackage.__init__(self, cache) - self.file = file - self.depends = [] - self.conflicts = [] - self.binaries = [] - if file != None: - self.open(file) - def getConflicts(self): - return self.conflicts - def getDepends(self): - return self.depends - def open(self, file): - depends_tags = ["Build-Depends:", "Build-Depends-Indep:"] - conflicts_tags = ["Build-Conflicts:", "Build-Conflicts-Indep:"] - for line in open(file): - # check b-d and b-c - for tag in depends_tags: - if line.startswith(tag): - key = line[len(tag):].strip() - self.depends.extend(apt_pkg.ParseSrcDepends(key)) - for tag in conflicts_tags: - if line.startswith(tag): - key = line[len(tag):].strip() - self.conflicts.extend(apt_pkg.ParseSrcDepends(key)) - # check binary and source and version - if line.startswith("Source:"): - self.pkgName = line[len("Source:"):].strip() - if line.startswith("Binary:"): - self.binaries = [pkg.strip() for pkg in line[len("Binary:"):].split(",")] - if line.startswith("Version:"): - self._sections["Version"] = line[len("Version:"):].strip() - # we are at the end - if line.startswith("-----BEGIN PGP SIGNATURE-"): - break - s = _("Install Build-Dependencies for " - "source package '%s' that builds %s\n" - ) % (self.pkgName, " ".join(self.binaries)) - self._sections["Description"] = s - - def checkDeb(self): - if not self.checkConflicts(): - for pkgname in self._installedConflicts: - if self._cache[pkgname]._pkg.Essential: - raise Exception, _("A essential package would be removed") - self._cache[pkgname].markDelete() - # FIXME: a additional run of the checkConflicts() - # after _satisfyDepends() should probably be done - return self._satisfyDepends(self.depends) - -if __name__ == "__main__": - - cache = Cache() - - vp = "www-browser" - print "%s virtual: %s" % (vp,cache.isVirtualPackage(vp)) - providers = cache.getProvidingPackages(vp) - print "Providers for %s :" % vp - for pkg in providers: - print " %s" % pkg.name - - d = DebPackage(cache, sys.argv[1]) - print "Deb: %s" % d.pkgName - if not d.checkDeb(): - print "can't be satified" - print d._failureString - print "missing deps: %s" % d.missingDeps - print d.requiredChanges - - print "Installing ..." - ret = d.install(DpkgInstallProgress()) - print ret - - #s = DscSrcPackage(cache, "../tests/3ddesktop_0.2.9-6.dsc") - #s.checkDep() - #print "Missing deps: ",s.missingDeps - #print "Print required changes: ", s.requiredChanges - - s = DscSrcPackage(cache) - d = "libc6 (>= 2.3.2), libaio (>= 0.3.96) | libaio1 (>= 0.3.96)" - print s._satisfyDepends(apt_pkg.ParseDepends(d)) - - -- cgit v1.2.3 From c03e8ae24331b8dce8d9b9865c0656f36dd429ad Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Sat, 23 Aug 2008 22:56:51 +0200 Subject: DebPackage: turn get(Provides|Replaces|Depends|Conflicts)() into attributes --- apt/debfile.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/apt/debfile.py b/apt/debfile.py index 36ad2594..745742bb 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -203,33 +203,89 @@ class DebPackage(object): return len(self._installedConflicts) != 0 def getConflicts(self): + """ + Return list of package names conflicting with this package. + + WARNING: This method will is deprecated. Please use the + attribute DebPackage.depends instead. + """ + warn.warning("Will is depricated. Please use the conflicts attribute") + return self.depends + + def conflicts(self): + """ + List of package names conflicting with this package + """ conflicts = [] key = "Conflicts" if self._sections.has_key(key): conflicts = apt_pkg.ParseDepends(self._sections[key]) return conflicts + conflicts = property(conflicts) def getDepends(self): + """ + Return list of package names on which this package depends on. + + WARNING: This method will is deprecated. Please use the + attribute DebPackage.depends instead. + """ + warn.warning("Will is depricated. Please use the depends attribute") + return self.depends + + def depends(self): + """ + List of package names on which this package depends on + """ depends = [] # find depends for key in ["Depends","PreDepends"]: if self._sections.has_key(key): depends.extend(apt_pkg.ParseDepends(self._sections[key])) return depends + depends = property(depends) def getProvides(self): + """ + Return list of virtual packages which are provided by this package. + + WARNING: This method will is deprecated. Please use the + attribute DebPackage.provides instead. + """ + warn.warning("Will is depricated. Please use the provides attribute") + self.provides + + def provides(self): + """ + List of virtual packages which are provided by this package + """ provides = [] key = "Provides" if self._sections.has_key(key): provides = apt_pkg.ParseDepends(self._sections[key]) return provides + provides = property(provides) def getReplaces(self): + """ + Return list of packages which are replaced by this package. + + WARNING: This method will is deprecated. Please use the + attribute DebPackage.replaces instead. + """ + warn.warning("Will is depricated. Please use the replaces attribute") + self.replaces + + def replaces(self): + """ + List of packages which are replaced by this package + """ replaces = [] key = "Replaces" if self._sections.has_key(key): replaces = apt_pkg.ParseDepends(self._sections[key]) return replaces + replaces = property(replaces) def replacesRealPkg(self, pkgname, oper, ver): """ @@ -366,7 +422,7 @@ class DebPackage(object): return self._needPkgs missingDeps = property(missingDeps) - def requiredChanges(self): + def requiredChanges(self, cache): """ gets the required changes to satisfy the depends. returns a tuple with (install, remove, unauthenticated) """ -- cgit v1.2.3 From ed4c10097afd41b6af0c5befcff3fa2bd78c82cd Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Sun, 24 Aug 2008 21:21:46 +0200 Subject: Minor fixes: Call InstallProgress.startUpdate() and InstallProgress.finishUpdate() for dpkg installations. Remove broken warnings about obsolete methods. Fix parameters of DebPackage.requiredChanges --- apt/debfile.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/apt/debfile.py b/apt/debfile.py index 745742bb..b5d2fea7 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -21,7 +21,6 @@ # import warnings -from warnings import warn warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning) import apt_inst, apt_pkg import apt @@ -209,7 +208,6 @@ class DebPackage(object): WARNING: This method will is deprecated. Please use the attribute DebPackage.depends instead. """ - warn.warning("Will is depricated. Please use the conflicts attribute") return self.depends def conflicts(self): @@ -230,7 +228,6 @@ class DebPackage(object): WARNING: This method will is deprecated. Please use the attribute DebPackage.depends instead. """ - warn.warning("Will is depricated. Please use the depends attribute") return self.depends def depends(self): @@ -252,8 +249,7 @@ class DebPackage(object): WARNING: This method will is deprecated. Please use the attribute DebPackage.provides instead. """ - warn.warning("Will is depricated. Please use the provides attribute") - self.provides + return self.provides def provides(self): """ @@ -273,8 +269,7 @@ class DebPackage(object): WARNING: This method will is deprecated. Please use the attribute DebPackage.replaces instead. """ - warn.warning("Will is depricated. Please use the replaces attribute") - self.replaces + return self.replaces def replaces(self): """ @@ -422,7 +417,7 @@ class DebPackage(object): return self._needPkgs missingDeps = property(missingDeps) - def requiredChanges(self, cache): + def requiredChanges(self): """ gets the required changes to satisfy the depends. returns a tuple with (install, remove, unauthenticated) """ @@ -455,7 +450,9 @@ class DebPackage(object): if installProgress == None: res = os.system("/usr/sbin/dpkg -i %s" % self.filename) else: + installProgress.startUpdate() res = installProgress.run(self.filename) + installProgress.finishUpdate() return res class DscSrcPackage(DebPackage): -- cgit v1.2.3 From e671440114f46b0c6a961cd995a0b7d8bfb3f2ba Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Sun, 24 Aug 2008 21:39:38 +0200 Subject: Revert the os._exit call in the child of the progress --- apt/progress.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apt/progress.py b/apt/progress.py index 2ef100a8..09a3fb06 100644 --- a/apt/progress.py +++ b/apt/progress.py @@ -214,7 +214,7 @@ class InstallProgress(DumbInstallProgress): if pid == 0: # child res = pm.DoInstall(self.writefd) - os.exit(res) + os._exit(res) self.child_pid = pid res = self.waitChild() return res @@ -249,7 +249,7 @@ class DpkgInstallProgress(InstallProgress): # child res = os.system("/usr/bin/dpkg --status-fd %s -i %s" % \ (self.writefd, self.debfile)) - os.exit(res) + os._exit(os.WEXITSTATUS(res)) self.child_pid = pid res = self.waitChild() return res -- cgit v1.2.3 From 07edbff3df9d15403256f1ef6d8e4bc92433182e Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Sun, 24 Aug 2008 21:59:17 +0200 Subject: Make version compare constants available globally and rename them for more consistency. --- apt/debfile.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/apt/debfile.py b/apt/debfile.py index b5d2fea7..9545ca98 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -30,6 +30,12 @@ from gettext import gettext as _ from cache import Cache from progress import DpkgInstallProgress +# Constants for comparing the local package file with the version in the cache +(VERSION_NONE, + VERSION_OUTDATED, + VERSION_SAME, + VERSION_NEWER) = range(4) + class NoDebArchiveException(IOError): pass @@ -313,12 +319,7 @@ class DebPackage(object): res = False return res - # some constants - (NO_VERSION, - VERSION_OUTDATED, - VERSION_SAME, - VERSION_IS_NEWER) = range(4) - + def compareToVersionInCache(self, useInstalled=True): """ checks if the pkg is already installed or availabe in the cache and if so in what version, returns if the version of the deb @@ -337,12 +338,12 @@ class DebPackage(object): cmp = apt_pkg.VersionCompare(cachever,debver) self._dbg(1, "CompareVersion(debver,instver): %s" % cmp) if cmp == 0: - return self.VERSION_SAME + return VERSION_SAME elif cmp < 0: - return self.VERSION_IS_NEWER + return VERSION_NEWER elif cmp > 0: - return self.VERSION_OUTDATED - return self.NO_VERSION + return VERSION_OUTDATED + return VERSION_NONE def checkDeb(self): self._dbg(3,"checkDepends") -- cgit v1.2.3 From bd6c809c2ea32c16c0222e82283dbe4b059ebbad Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Sun, 24 Aug 2008 22:18:13 +0200 Subject: Fix use of previously changed constants in checkDeb --- apt/debfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apt/debfile.py b/apt/debfile.py index 9545ca98..4fcd6ec0 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -357,7 +357,7 @@ class DebPackage(object): # check version res = self.compareToVersionInCache() - if res == self.VERSION_OUTDATED: # the deb is older than the installed + if res == VERSION_OUTDATED: # the deb is older than the installed self._failureString = _("A later version is already installed") return False -- cgit v1.2.3 From 59fe10665676e2e6142cada2580064c5fa881b6e Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Sun, 24 Aug 2008 22:20:00 +0200 Subject: Import gtk progresses from gdebi --- apt/gtk.py | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 apt/gtk.py diff --git a/apt/gtk.py b/apt/gtk.py new file mode 100644 index 00000000..cb635e87 --- /dev/null +++ b/apt/gtk.py @@ -0,0 +1,125 @@ +# GtkProgress.py +# +# Copyright (c) 2004,2005 Canonical +# +# 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 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 + +import pygtk +pygtk.require('2.0') +import gtk +import apt +import apt_pkg +from gettext import gettext as _ +from Common.utils import * + +# intervals of the start up progress +# 3x caching and menu creation +STEPS_UPDATE_CACHE = [33, 66, 100] + +class GtkOpProgress(apt.OpProgress): + def __init__(self, host_window, progressbar, status, parent, + steps=STEPS_UPDATE_CACHE): + # used for the "one run progressbar" + self.steps = steps[:] + self.base = 0 + self.old = 0 + self.next = int(self.steps.pop(0)) + + self._parent = parent + self._window = host_window + self._status = status + self._progressbar = progressbar + # Do not show the close button + self._window.realize() + host_window.window.set_functions(gtk.gdk.FUNC_MOVE) + self._window.set_transient_for(parent) + + def update(self, percent): + #print percent + #print self.Op + #print self.SubOp + self._window.show() + self._parent.set_sensitive(False) + # if the old percent was higher, a new progress was started + if self.old > percent: + # set the borders to the next interval + self.base = self.next + try: + self.next = int(self.steps.pop(0)) + except: + pass + progress = self.base + percent/100 * (self.next - self.base) + self.old = percent + self._status.set_markup("%s" % self.op) + self._progressbar.set_fraction(progress/100.0) + while gtk.events_pending(): + gtk.main_iteration() + + def done(self): + self._parent.set_sensitive(True) + def hide(self): + self._window.hide() + +class GtkFetchProgress(apt.progress.FetchProgress): + def __init__(self, parent, summary="", descr=""): + # if this is set to false the download will cancel + self._continue = True + # init vars here + # FIXME: find a more elegant way, this sucks + self.summary = parent.label_fetch_summary + self.status = parent.label_fetch_status + self.progress = parent.progressbar_fetch + self.window_fetch = parent.window_fetch + self.window_fetch.set_transient_for(parent.window_main) + self.window_fetch.realize() + self.window_fetch.window.set_functions(gtk.gdk.FUNC_MOVE) + # set summary + if self.summary != "": + self.summary.set_markup("%s \n\n%s" % + (summary, descr)) + def start(self): + self.progress.set_fraction(0) + self.window_fetch.show() + def stop(self): + self.window_fetch.hide() + def on_button_fetch_cancel_clicked(self, widget): + self._continue = False + def pulse(self): + apt.progress.FetchProgress.pulse(self) + currentItem = self.currentItems + 1 + if currentItem > self.totalItems: + currentItem = self.totalItems + if self.currentCPS > 0: + statusText = (_("Downloading file %(current)li of %(total)li with " + "%(speed)s/s") % {"current" : currentItem, + "total" : self.totalItems, + "speed" : humanize_size(self.currentCPS)}) + else: + statusText = (_("Downloading file %(current)li of %(total)li") % \ + {"current" : currentItem, + "total" : self.totalItems }) + self.progress.set_fraction(self.percent/100.0) + self.status.set_markup("%s" % statusText) + # TRANSLATORS: show the remaining time in a progress bar: + #self.progress.set_text(_("About %s left" % (apt_pkg.TimeToStr(self.eta)))) + # FIXME: show remaining time + self.progress.set_text("") + + while gtk.events_pending(): + gtk.main_iteration() + return self._continue -- cgit v1.2.3 From 423a25e57a4c07c2803ef39e2d0d7a2b73d5ab7c Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Tue, 26 Aug 2008 20:01:59 +0200 Subject: Rename gtk to widgets. Replace the GDebi progresses by gobject based ones and add GtkAptProgress as a general progress providing widget. --- apt/gtk.py | 125 ------------------- apt/widgets.py | 372 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 372 insertions(+), 125 deletions(-) delete mode 100644 apt/gtk.py create mode 100644 apt/widgets.py diff --git a/apt/gtk.py b/apt/gtk.py deleted file mode 100644 index cb635e87..00000000 --- a/apt/gtk.py +++ /dev/null @@ -1,125 +0,0 @@ -# GtkProgress.py -# -# Copyright (c) 2004,2005 Canonical -# -# 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 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 - -import pygtk -pygtk.require('2.0') -import gtk -import apt -import apt_pkg -from gettext import gettext as _ -from Common.utils import * - -# intervals of the start up progress -# 3x caching and menu creation -STEPS_UPDATE_CACHE = [33, 66, 100] - -class GtkOpProgress(apt.OpProgress): - def __init__(self, host_window, progressbar, status, parent, - steps=STEPS_UPDATE_CACHE): - # used for the "one run progressbar" - self.steps = steps[:] - self.base = 0 - self.old = 0 - self.next = int(self.steps.pop(0)) - - self._parent = parent - self._window = host_window - self._status = status - self._progressbar = progressbar - # Do not show the close button - self._window.realize() - host_window.window.set_functions(gtk.gdk.FUNC_MOVE) - self._window.set_transient_for(parent) - - def update(self, percent): - #print percent - #print self.Op - #print self.SubOp - self._window.show() - self._parent.set_sensitive(False) - # if the old percent was higher, a new progress was started - if self.old > percent: - # set the borders to the next interval - self.base = self.next - try: - self.next = int(self.steps.pop(0)) - except: - pass - progress = self.base + percent/100 * (self.next - self.base) - self.old = percent - self._status.set_markup("%s" % self.op) - self._progressbar.set_fraction(progress/100.0) - while gtk.events_pending(): - gtk.main_iteration() - - def done(self): - self._parent.set_sensitive(True) - def hide(self): - self._window.hide() - -class GtkFetchProgress(apt.progress.FetchProgress): - def __init__(self, parent, summary="", descr=""): - # if this is set to false the download will cancel - self._continue = True - # init vars here - # FIXME: find a more elegant way, this sucks - self.summary = parent.label_fetch_summary - self.status = parent.label_fetch_status - self.progress = parent.progressbar_fetch - self.window_fetch = parent.window_fetch - self.window_fetch.set_transient_for(parent.window_main) - self.window_fetch.realize() - self.window_fetch.window.set_functions(gtk.gdk.FUNC_MOVE) - # set summary - if self.summary != "": - self.summary.set_markup("%s \n\n%s" % - (summary, descr)) - def start(self): - self.progress.set_fraction(0) - self.window_fetch.show() - def stop(self): - self.window_fetch.hide() - def on_button_fetch_cancel_clicked(self, widget): - self._continue = False - def pulse(self): - apt.progress.FetchProgress.pulse(self) - currentItem = self.currentItems + 1 - if currentItem > self.totalItems: - currentItem = self.totalItems - if self.currentCPS > 0: - statusText = (_("Downloading file %(current)li of %(total)li with " - "%(speed)s/s") % {"current" : currentItem, - "total" : self.totalItems, - "speed" : humanize_size(self.currentCPS)}) - else: - statusText = (_("Downloading file %(current)li of %(total)li") % \ - {"current" : currentItem, - "total" : self.totalItems }) - self.progress.set_fraction(self.percent/100.0) - self.status.set_markup("%s" % statusText) - # TRANSLATORS: show the remaining time in a progress bar: - #self.progress.set_text(_("About %s left" % (apt_pkg.TimeToStr(self.eta)))) - # FIXME: show remaining time - self.progress.set_text("") - - while gtk.events_pending(): - gtk.main_iteration() - return self._continue diff --git a/apt/widgets.py b/apt/widgets.py new file mode 100644 index 00000000..09236d21 --- /dev/null +++ b/apt/widgets.py @@ -0,0 +1,372 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +widgets - GTK widgets to show the progress and status of apt + +Copyright (c) 2004,2005 Canonical Ltd. + +Authors: Michael Vogt + Sebastian Heinlein + +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. + +his 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 +""" + +from gettext import gettext as _ +import os +import time + +import pygtk +pygtk.require('2.0') +import gtk +import gobject +import pango +import vte + +import apt +import apt_pkg + +# Seconds until a maintainer script will be regarded as hanging +INSTALL_TIMEOUT = 4 * 60 + +class GOpProgress(gobject.GObject, apt.progress.OpProgress): + + __gsignals__ = {"status-changed":(gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + (gobject.TYPE_STRING, gobject.TYPE_INT)), + "status-started":(gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ()), + "status-finished":(gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ())} + + def __init__(self): + apt.progress.OpProgress.__init__(self) + gobject.GObject.__init__(self) + + def update(self, percent): + self.emit("status-changed", self.op, percent) + while gtk.events_pending(): + gtk.main_iteration() + + def done(self): + self.emit("status-finished") + +class GInstallProgress(gobject.GObject, apt.progress.InstallProgress): + + __gsignals__ = {"status-changed":(gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + (gobject.TYPE_STRING, gobject.TYPE_INT)), + "status-started":(gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ()), + "status-timeout":(gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ()), + "status-error":(gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ()), + "status-conffile":(gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ()), + "status-finished":(gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ())} + + def __init__(self, term): + apt.progress.InstallProgress.__init__(self) + gobject.GObject.__init__(self) + self.finished = False + self.time_last_update = time.time() + self.term = term + reaper = vte.reaper_get() + reaper.connect("child-exited", self.childExited) + self.env = ["VTE_PTY_KEEP_FD=%s"% self.writefd, + "DEBIAN_FRONTEND=gnome", + "APT_LISTCHANGES_FRONTEND=gtk"] + + def childExited(self, term, pid, status): + self.apt_status = os.WEXITSTATUS(status) + self.finished = True + + def error(self, pkg, errormsg): + self.emit("status-error") + + def conffile(self, current, new): + self.emit("status-conffile") + + def startUpdate(self): + self.emit("status-started") + + def finishUpdate(self): + self.emit("status-finished") + + def statusChange(self, pkg, percent, status): + self.time_last_update = time.time() + self.emit("status-changed", status, percent) + + def updateInterface(self): + apt.progress.InstallProgress.updateInterface(self) + while gtk.events_pending(): + gtk.main_iteration() + if self.time_last_update + INSTALL_TIMEOUT < time.time(): + self.emit("status-timeout") + + def fork(self): + return self.term.forkpty(envv=self.env) + + def waitChild(self): + while not self.finished: + self.updateInterface() + return self.apt_status + + +class GDpkgInstallProgress(apt.progress.DpkgInstallProgress,GInstallProgress): + + def run(self, debfile): + apt.progress.DpkgInstallProgress.run(self, debfile) + + def updateInterface(self): + apt.progress.DpkgInstallProgress.updateInterface(self) + while gtk.events_pending(): + gtk.main_iteration() + if self.time_last_update + INSTALL_TIMEOUT < time.time(): + self.emit("status-timeout") + + +class GFetchProgress(gobject.GObject, apt.progress.FetchProgress): + + __gsignals__ = {"status-changed":(gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + (gobject.TYPE_STRING, gobject.TYPE_INT)), + "status-started":(gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ()), + "status-finished":(gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ())} + + def __init__(self): + apt.progress.FetchProgress.__init__(self) + gobject.GObject.__init__(self) + self._continue = True + + def start(self): + self.emit("status-started") + + def stop(self): + self.emit("status-finished") + + def cancel(self): + self._continue = False + + def pulse(self): + apt.progress.FetchProgress.pulse(self) + currentItem = self.currentItems + 1 + if currentItem > self.totalItems: + currentItem = self.totalItems + if self.currentCPS > 0: + text = (_("Downloading file %(current)li of %(total)li with " + "%(speed)s/s") % \ + {"current" : currentItem, + "total" : self.totalItems, + "speed" : humanize_size(self.currentCPS)}) + else: + text = (_("Downloading file %(current)li of %(total)li") % \ + {"current" : currentItem, + "total" : self.totalItems }) + self.emit("status-changed", text, self.percent) + while gtk.events_pending(): + gtk.main_iteration() + return self._continue + + +class GtkAptProgress(gtk.VBox): + """ + This widget provides a progress bar, a terminal and a status bar for + showing the progress of package manipulation tasks. + + A simple example code snippet to install/remove a package: + + import pygtk + pygtk.require('2.0') + import gtk + + import apt.widgets + + win = gtk.Window() + progress = apt.widgets.GtkAptProgress() + win.set_title("GtkAptProgress Demo") + win.add(progress) + progress.show() + win.show() + + cache = apt.cache.Cache(progress.get_open_progress()) + cache["xterm"].markDelete() + progress.show_terminal(expanded=True) + cache.commit(progress.get_fetch_progress(), + progress.get_install_progress()) + + gtk.main() + """ + def __init__(self): + gtk.VBox.__init__(self) + self.set_spacing(6) + # Setup some child widgets + self._expander = gtk.Expander(_("Details")) + self._terminal = vte.Terminal() + #self._terminal.set_font_from_string("monospace 10") + self._expander.add(self._terminal) + self._progressbar = gtk.ProgressBar() + # Setup the always italic status label + self._label = gtk.Label() + attr_list = pango.AttrList() + attr_list.insert(pango.AttrStyle(pango.STYLE_ITALIC, 0, -1)) + self._label.set_attributes(attr_list) + self._label.set_ellipsize(pango.ELLIPSIZE_END) + self._label.set_alignment(0, 0) + # add child widgets + self.pack_start(self._progressbar, False) + self.pack_start(self._label, False) + self.pack_start(self._expander, False) + # Setup the internal progress handlers + self._progress_open = GOpProgress() + self._progress_open.connect("status-changed", self._on_status_changed) + self._progress_open.connect("status-started", self._on_status_started) + self._progress_open.connect("status-finished", self._on_status_finished) + self._progress_fetch = GFetchProgress() + self._progress_fetch.connect("status-changed", self._on_status_changed) + self._progress_fetch.connect("status-started", self._on_status_started) + self._progress_fetch.connect("status-finished", + self._on_status_finished) + self._progress_install = GInstallProgress(self._terminal) + self._progress_install.connect("status-changed", + self._on_status_changed) + self._progress_install.connect("status-started", + self._on_status_started) + self._progress_install.connect("status-finished", + self._on_status_finished) + self._progress_install.connect("status-timeout", + self._on_status_timeout) + self._progress_install.connect("status-error", + self._on_status_timeout) + self._progress_install.connect("status-conffile", + self._on_status_timeout) + self._progress_dpkg_install = GDpkgInstallProgress(self._terminal) + self._progress_dpkg_install.connect("status-changed", + self._on_status_changed) + self._progress_dpkg_install.connect("status-started", + self._on_status_started) + self._progress_dpkg_install.connect("status-finished", + self._on_status_finished) + self._progress_dpkg_install.connect("status-timeout", + self._on_status_timeout) + self._progress_dpkg_install.connect("status-error", + self._on_status_timeout) + self._progress_dpkg_install.connect("status-conffile", + self._on_status_timeout) + + def clear(self): + """ + Reset all status information + """ + self._label.set_label("") + self._progress.set_fraction(0) + self._expander.set_expanded(False) + + def get_open_progress(self): + """ + Return the cache opening progress handler. + """ + return self._progress_open + + def get_install_progress(self): + """ + Return the install progress handler + """ + return self._progress_install + + def get_dpkg_install_progress(self): + """ + Return the install progress handler for dpkg + """ + return self._dpkg_progress_install + + def get_fetch_progress(self): + """ + Return the fetch progress handler + """ + return self._progress_fetch + + def _on_status_started(self, progress): + self._on_status_changed(progress, _("Starting..."), 0) + + def _on_status_finished(self, progress): + self._on_status_changed(progress, _("Complete"), 100) + + def _on_status_changed(self, progress, status, percent): + self._label.set_text(status) + self._progressbar.pulse() + self._progressbar.set_fraction(percent/100.0) + + def _on_status_timeout(self, progress): + selt._expander.set_expanded(True) + + def cancel_download(self): + """ + Cancel a currently running download + """ + self._progress_fetch.cancel() + + def show_terminal(self, expanded=False): + """ + Show an expander with a terminal widget which provides a way + to interact with dpkg + """ + self._expander.show() + self._terminal.show() + self._expander.set_expanded(True) + + def hide_terminal(self): + """ + Hide the expander with the terminal widget + """ + self._expander.hide() + + def show(self): + gtk.HBox.show(self) + self._label.show() + self._progressbar.show() + +if __name__ == "__main__": + import sys + import debfile + + win = gtk.Window() + apt_progress = GtkAptProgress() + win.set_title("GtkAptProgress Demo") + win.add(apt_progress) + apt_progress.show() + win.show() + cache = apt.cache.Cache(apt_progress.get_open_progress()) + pkg = cache["xterm"] + if pkg.isInstalled: + pkg.markDelete() + else: + pkg.markInstall() + apt_progress.show_terminal(True) + try: + cache.commit(apt_progress.get_fetch_progress(), + apt_progress.get_install_progress()) + except: + pass + if len(sys.argv) > 1: + deb = DebPackage(sys.argv[1], cache) + deb.install(apt_progress.get_dpkg_install_progress()) + gtk.main() + +# vim: ts=4 et sts=4 -- cgit v1.2.3 From 9f4ab37f5f7736c112fbbb1c157321cdc3c0570f Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Wed, 27 Aug 2008 14:48:14 +0200 Subject: For empt --- apt/package.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apt/package.py b/apt/package.py index 3b26fb2b..3eb692d8 100644 --- a/apt/package.py +++ b/apt/package.py @@ -376,16 +376,19 @@ class Package(object): installedSize = property(installedSize) def installedFiles(self): - """ Return the list of files installed on the system by the package """ + """ + Return the list of unicode names of the files which have + been installed by this package + """ if not self.isInstalled: - return None + return [] path = "/var/lib/dpkg/info/%s.list" % self.name try: list = open(path) - files = list.read().split("\n") + files = list.read().decode().split("\n") list.close() except: - return None + return [] return files installedFiles = property(installedFiles) -- cgit v1.2.3 From bfad41ee6621f13278b29dda8a2c12997c23e9d9 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Thu, 28 Aug 2008 08:56:14 +0200 Subject: Fix: not installed files can have installed files --- apt/package.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/apt/package.py b/apt/package.py index 3eb692d8..f63c13de 100644 --- a/apt/package.py +++ b/apt/package.py @@ -380,8 +380,6 @@ class Package(object): Return the list of unicode names of the files which have been installed by this package """ - if not self.isInstalled: - return [] path = "/var/lib/dpkg/info/%s.list" % self.name try: list = open(path) -- cgit v1.2.3 From b9b064ea89b5948a11b2ab91188eaafb6610d114 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 29 Aug 2008 10:19:01 +0200 Subject: Remove not used imports in debfile --- apt/debfile.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apt/debfile.py b/apt/debfile.py index 4fcd6ec0..a0a8e016 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -23,12 +23,9 @@ import warnings warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning) import apt_inst, apt_pkg -import apt import sys import os from gettext import gettext as _ -from cache import Cache -from progress import DpkgInstallProgress # Constants for comparing the local package file with the version in the cache (VERSION_NONE, @@ -507,6 +504,8 @@ class DscSrcPackage(DebPackage): return self._satisfyDepends(self.depends) if __name__ == "__main__": + from cache import Cache + from progress import DpkgInstallProgress cache = Cache() -- cgit v1.2.3 From e260803ebac317471ad185e2efffd1c4b09c887c Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 29 Aug 2008 10:19:47 +0200 Subject: debfile: fix a silly error that made me some headache and returned the depends in getConflicts() --- apt/debfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apt/debfile.py b/apt/debfile.py index a0a8e016..b1d436cd 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -211,7 +211,7 @@ class DebPackage(object): WARNING: This method will is deprecated. Please use the attribute DebPackage.depends instead. """ - return self.depends + return self.conflicts def conflicts(self): """ -- cgit v1.2.3 From 4d6968553b8d7ac4faa436736a1846406fa4ec8f Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Sat, 30 Aug 2008 10:35:36 +0200 Subject: Reformat the package description according to the Debian Policy --- apt/package.py | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/apt/package.py b/apt/package.py index f63c13de..01e6057b 100644 --- a/apt/package.py +++ b/apt/package.py @@ -245,7 +245,10 @@ class Package(object): def description(self, format=True): """ - Return the formated long description + Return the formated long description according to the Debian policy + (Chapter 5.6.13). + See http://www.debian.org/doc/debian-policy/ch-controlfields.html + for more information. """ if not self._lookupRecord(): return "" @@ -263,19 +266,32 @@ class Package(object): for i in range(len(lines)): # Skip the first line, since its a duplication of the summary if i == 0: continue - line = lines[i].strip() - # Replace all empty lines by line breaks - if line == ".": - desc += "\n" + raw_line = lines[i] + if raw_line.strip() == ".": + # The line is just line break + if not desc.endswith("\n"): + desc += "\n" continue - # Use dots for lists - p = re.compile(r'^(\s|\t)*(\*|0|-)',re.MULTILINE) - line = p.sub(ur'\n\u2022', line) - # Use line breaks only for abstracts - if desc == "" or desc[-1] == "\n": - desc += line + elif raw_line.startswith(" "): + # The line should be displayed verbatim without word wrapping + if not desc.endswith("\n"): + line = "\n%s\n" % raw_line[2:] + else: + line = "%s\n" % raw_line[2:] + elif raw_line.startswith(" "): + # The line is part of a paragraph. + if desc.endswith("\n") or desc == "": + # Skip the leading white space + line = raw_line[1:] + else: + line = raw_line else: - desc += " " + line + pkglog.debug("invalid line %s in description for %s:\n%s" % \ + (i, self.name, self.rawDescription)) + # Use dots for lists + line = re.sub(r"^(\s*)(\*|0|o|-) ", ur"\1\u2022 ", line, 1) + # Add current line to the description + desc += line return desc description = property(description) -- cgit v1.2.3 From d7b8902c36f77b7ae87b88cb4fdfbba7aa2ff833 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Sat, 30 Aug 2008 10:39:12 +0200 Subject: Remove logging statement. (Was c&p error from PackageKit) --- apt/package.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apt/package.py b/apt/package.py index 01e6057b..c25f1be9 100644 --- a/apt/package.py +++ b/apt/package.py @@ -286,8 +286,7 @@ class Package(object): else: line = raw_line else: - pkglog.debug("invalid line %s in description for %s:\n%s" % \ - (i, self.name, self.rawDescription)) + line = raw_line # Use dots for lists line = re.sub(r"^(\s*)(\*|0|o|-) ", ur"\1\u2022 ", line, 1) # Add current line to the description -- cgit v1.2.3 From 53f6d225f75a4a49eba0cfdf9ce8b7cd62074c1d Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Mon, 24 Nov 2008 15:16:40 +0100 Subject: change naming for the gobjects to match glatzors branch --- apt/gtk/widgets.py | 47 +++++++++++++++++++++++++++++++++-------------- debian/changelog | 8 ++++---- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/apt/gtk/widgets.py b/apt/gtk/widgets.py index 876f7a9e..6f55644f 100644 --- a/apt/gtk/widgets.py +++ b/apt/gtk/widgets.py @@ -38,7 +38,12 @@ import vte import apt import apt_pkg -class GtkOpProgress(gobject.GObject, apt.progress.OpProgress): +# FIXME: we should not use gtk.events_pending(): gtk.main_iteration() +# in the gobjects. instead we should use +# g_main_context_pending/g_main_context_iteration +# but that is not available yet in python-gobject + +class GOpProgress(gobject.GObject, apt.progress.OpProgress): __gsignals__ = {"status-changed":(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, @@ -60,7 +65,7 @@ class GtkOpProgress(gobject.GObject, apt.progress.OpProgress): def done(self): self.emit("status-finished") -class GtkInstallProgress(gobject.GObject, apt.progress.InstallProgress): +class GInstallProgress(gobject.GObject, apt.progress.InstallProgress): # Seconds until a maintainer script will be regarded as hanging INSTALL_TIMEOUT = 5 * 60 @@ -127,20 +132,18 @@ class GtkInstallProgress(gobject.GObject, apt.progress.InstallProgress): return self.apt_status -class GtkDpkgInstallProgress(apt.progress.DpkgInstallProgress,GtkInstallProgress): +class GDpkgInstallProgress(apt.progress.DpkgInstallProgress,GInstallProgress): def run(self, debfile): apt.progress.DpkgInstallProgress.run(self, debfile) def updateInterface(self): apt.progress.DpkgInstallProgress.updateInterface(self) - while gtk.events_pending(): - gtk.main_iteration() if self.time_last_update + self.INSTALL_TIMEOUT < time.time(): self.emit("status-timeout") -class GtkFetchProgress(gobject.GObject, apt.progress.FetchProgress): +class GFetchProgress(gobject.GObject, apt.progress.FetchProgress): __gsignals__ = {"status-changed":(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, @@ -234,16 +237,16 @@ class GtkAptProgress(gtk.VBox): self.pack_start(self._label, False) self.pack_start(self._expander, False) # Setup the internal progress handlers - self._progress_open = GtkOpProgress() + self._progress_open = GOpProgress() self._progress_open.connect("status-changed", self._on_status_changed) self._progress_open.connect("status-started", self._on_status_started) self._progress_open.connect("status-finished", self._on_status_finished) - self._progress_fetch = GtkFetchProgress() + self._progress_fetch = GFetchProgress() self._progress_fetch.connect("status-changed", self._on_status_changed) self._progress_fetch.connect("status-started", self._on_status_started) self._progress_fetch.connect("status-finished", self._on_status_finished) - self._progress_install = GtkInstallProgress(self._terminal) + self._progress_install = GInstallProgress(self._terminal) self._progress_install.connect("status-changed", self._on_status_changed) self._progress_install.connect("status-started", @@ -256,7 +259,7 @@ class GtkAptProgress(gtk.VBox): self._on_status_timeout) self._progress_install.connect("status-conffile", self._on_status_timeout) - self._progress_dpkg_install = GtkDpkgInstallProgress(self._terminal) + self._progress_dpkg_install = GDpkgInstallProgress(self._terminal) self._progress_dpkg_install.connect("status-changed", self._on_status_changed) self._progress_dpkg_install.connect("status-started", @@ -308,17 +311,27 @@ class GtkAptProgress(gtk.VBox): def _on_status_started(self, progress): self._on_status_changed(progress, _("Starting..."), 0) + while gtk.events_pending(): + gtk.main_iteration() def _on_status_finished(self, progress): self._on_status_changed(progress, _("Complete"), 100) + while gtk.events_pending(): + gtk.main_iteration() def _on_status_changed(self, progress, status, percent): self._label.set_text(status) - self._progressbar.pulse() - self._progressbar.set_fraction(percent/100.0) + if percent is None: + self._progressbar.pulse() + else: + self._progressbar.set_fraction(percent/100.0) + while gtk.events_pending(): + gtk.main_iteration() def _on_status_timeout(self, progress): selt._expander.set_expanded(True) + while gtk.events_pending(): + gtk.main_iteration() def cancel_download(self): """ @@ -333,25 +346,31 @@ class GtkAptProgress(gtk.VBox): """ self._expander.show() self._terminal.show() - self._expander.set_expanded(True) + self._expander.set_expanded(expanded) + while gtk.events_pending(): + gtk.main_iteration() def hide_terminal(self): """ Hide the expander with the terminal widget """ self._expander.hide() + while gtk.events_pending(): + gtk.main_iteration() def show(self): gtk.HBox.show(self) self._label.show() self._progressbar.show() + while gtk.events_pending(): + gtk.main_iteration() if __name__ == "__main__": import sys import debfile win = gtk.Window() - apt_progress = GtkAptProgress() + apt_progress = GAptProgress() win.set_title("GtkAptProgress Demo") win.add(apt_progress) apt_progress.show() diff --git a/debian/changelog b/debian/changelog index 8efc99bf..e812fe32 100644 --- a/debian/changelog +++ b/debian/changelog @@ -14,10 +14,10 @@ python-apt (0.7.9) UNRELEASED; urgency=low - new method "installedFiles()" - new method "getChangelog()" * apt/gtk/widgets.py: - - new widget GtkOpProgress - - new widget GtkFetchProgress - - new widget GtkInstallProgress - - new widget GtkDpkgInstallProgress + - new gobject GOpProgress + - new gobject GFetchProgress + - new gobject GInstallProgress + - new gobject GDpkgInstallProgress - new widget GtkAptProgress * doc/examples/gui-inst.py: - updated to use the new widgets -- cgit v1.2.3 From 037edcfd52130c0db375f19867b25ffe46f40f94 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Mon, 24 Nov 2008 15:24:14 +0100 Subject: gtk.events_pending() -> glib.main_context_default().pending() --- apt/gtk/widgets.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/apt/gtk/widgets.py b/apt/gtk/widgets.py index 6f55644f..3a15258f 100644 --- a/apt/gtk/widgets.py +++ b/apt/gtk/widgets.py @@ -31,6 +31,7 @@ import time import pygtk pygtk.require('2.0') import gtk +import glib import gobject import pango import vte @@ -38,11 +39,6 @@ import vte import apt import apt_pkg -# FIXME: we should not use gtk.events_pending(): gtk.main_iteration() -# in the gobjects. instead we should use -# g_main_context_pending/g_main_context_iteration -# but that is not available yet in python-gobject - class GOpProgress(gobject.GObject, apt.progress.OpProgress): __gsignals__ = {"status-changed":(gobject.SIGNAL_RUN_FIRST, @@ -56,11 +52,12 @@ class GOpProgress(gobject.GObject, apt.progress.OpProgress): def __init__(self): apt.progress.OpProgress.__init__(self) gobject.GObject.__init__(self) + self._context = glib.main_context_default() def update(self, percent): self.emit("status-changed", self.op, percent) - while gtk.events_pending(): - gtk.main_iteration() + while self._context.pending(): + self._context.iteration() def done(self): self.emit("status-finished") @@ -95,6 +92,7 @@ class GInstallProgress(gobject.GObject, apt.progress.InstallProgress): self.env = ["VTE_PTY_KEEP_FD=%s"% self.writefd, "DEBIAN_FRONTEND=gnome", "APT_LISTCHANGES_FRONTEND=gtk"] + self._context = glib.main_context_default() def childExited(self, term, pid, status): self.apt_status = os.WEXITSTATUS(status) @@ -118,8 +116,8 @@ class GInstallProgress(gobject.GObject, apt.progress.InstallProgress): def updateInterface(self): apt.progress.InstallProgress.updateInterface(self) - while gtk.events_pending(): - gtk.main_iteration() + while self._context.pending(): + self._context.iteration() if self.time_last_update + self.INSTALL_TIMEOUT < time.time(): self.emit("status-timeout") @@ -157,6 +155,7 @@ class GFetchProgress(gobject.GObject, apt.progress.FetchProgress): apt.progress.FetchProgress.__init__(self) gobject.GObject.__init__(self) self._continue = True + self._context = glib.main_context_default() def start(self): self.emit("status-started") @@ -183,8 +182,8 @@ class GFetchProgress(gobject.GObject, apt.progress.FetchProgress): {"current" : currentItem, "total" : self.totalItems }) self.emit("status-changed", text, self.percent) - while gtk.events_pending(): - gtk.main_iteration() + while self._context.pending(): + self._context.iteration() return self._continue -- cgit v1.2.3 From 4dcfce5fd58b99b9aaf6acb2ab86d811b1f1a01d Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 11 Dec 2008 10:42:00 -0800 Subject: - new method "getRequiredDownload()" - new method "additionalRequiredSpace()" --- apt/cache.py | 11 +++++++++++ debian/changelog | 2 ++ 2 files changed, 13 insertions(+) diff --git a/apt/cache.py b/apt/cache.py index 3c032052..79e58282 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -129,6 +129,17 @@ class Cache(object): self.cachePostChange() @property + def requiredDownload(self): + """ get the size of the packages that are required to download """ + pm = apt_pkg.GetPackageManager(self._depcache) + fetcher = apt_pkg.GetAcquire() + pm.GetArchives(fetcher, self._list, self._records) + return fetcher.FetchNeeded + @property + def additionalRequiredSpace(self): + """ get the size of the additional required space on the fs """ + return self._depcache.UsrSize + @property def reqReinstallPkgs(self): " return the packages not downloadable packages in reqreinst state " reqreinst = set() diff --git a/debian/changelog b/debian/changelog index e812fe32..3c3ae251 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,8 @@ python-apt (0.7.9) UNRELEASED; urgency=low * apt/cache.py: - new method "isVirtualPackage()" - new method "getProvidingPackages()" + - new method "getRequiredDownload()" + - new method "additionalRequiredSpace()" * apt/debfile.py: - move a lot of the gdebi code into this file, this provides interfaces for querrying and installing -- cgit v1.2.3 From 481a90a820564dc12567348c16773e15cc129560 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Mon, 15 Dec 2008 14:24:51 +0100 Subject: * setup.py: - build html/ help of the apt and aptsources modules into /usr/share/doc/python-apt/html --- apt/package.py | 7 +++++-- debian/changelog | 3 +++ debian/python-apt.docs | 1 + debian/rules | 3 +++ setup.py | 29 ++++++++++++++++++++++++++++- 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/apt/package.py b/apt/package.py index 94242e35..70ddbb1a 100644 --- a/apt/package.py +++ b/apt/package.py @@ -244,13 +244,15 @@ class Package(object): return self._records.ShortDesc summary = property(summary) - def description(self, format=True): + def description(self, format=True, useDots=False): """ Return the formated long description according to the Debian policy (Chapter 5.6.13). See http://www.debian.org/doc/debian-policy/ch-controlfields.html for more information. """ + if not format: + return self.rawDescription if not self._lookupRecord(): return "" # get the translated description @@ -289,7 +291,8 @@ class Package(object): else: line = raw_line # Use dots for lists - line = re.sub(r"^(\s*)(\*|0|o|-) ", ur"\1\u2022 ", line, 1) + if useDots: + line = re.sub(r"^(\s*)(\*|0|o|-) ", ur"\1\u2022 ", line, 1) # Add current line to the description desc += line return desc diff --git a/debian/changelog b/debian/changelog index 3c3ae251..160fe7c8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -25,6 +25,9 @@ python-apt (0.7.9) UNRELEASED; urgency=low - updated to use the new widgets * debian/control: - add suggests for python-gtk2 and python-vte + * setup.py: + - build html/ help of the apt and aptsources modules + into /usr/share/doc/python-apt/html -- Michael Vogt Mon, 24 Nov 2008 14:30:32 +0100 diff --git a/debian/python-apt.docs b/debian/python-apt.docs index 208050c5..f3c87fdc 100644 --- a/debian/python-apt.docs +++ b/debian/python-apt.docs @@ -1,3 +1,4 @@ README apt/README.apt data/templates/README.templates +html/ diff --git a/debian/rules b/debian/rules index c8aff2f9..adaad2a8 100755 --- a/debian/rules +++ b/debian/rules @@ -21,6 +21,9 @@ build/python-apt-dbg:: python$$i-dbg ./setup.py build; \ done +build/python-apt:: + pydoc -w + install/python-apt-dbg:: for i in $(cdbs_python_build_versions); do \ python$$i-dbg ./setup.py install --root $(CURDIR)/debian/python-apt-dbg; \ diff --git a/setup.py b/setup.py index fcbb6ba4..7024f81e 100755 --- a/setup.py +++ b/setup.py @@ -4,7 +4,30 @@ from distutils.core import setup, Extension from distutils.sysconfig import parse_makefile from DistUtilsExtra.command import * -import glob, os, string +import glob +import os +import os.path +import pydoc +import shutil +import string +import sys + +def build_docs(dir="html", modules=["apt","aptsources"]): + htmldir = os.path.join(os.getcwd(), dir) + for d in modules: + for (dirpath, dirnames, filenames) in os.walk(d): + pydoc.writedoc(dirpath.replace("/",".")) + for file in filenames: + if not file.endswith(".py"): + continue + if file in ["__init__.py"]: + continue + pydoc.writedoc(dirpath.replace("/",".")+"."+file.split(".py")[0]) + if not os.path.exists(htmldir): + os.mkdir(htmldir) + for f in glob.glob("*.html"): + shutil.move(f, htmldir) + return True # The apt_pkg module files = map(lambda source: "python/"+source, @@ -29,6 +52,10 @@ for template in glob.glob('data/templates/*.info.in'): source.close() build.close() +# build doc +if sys.argv[1] == "build": + build_docs() + setup(name="python-apt", version="0.6.17", description="Python bindings for APT", -- cgit v1.2.3