summaryrefslogtreecommitdiff
path: root/apt/progress
diff options
context:
space:
mode:
authorJulian Andres Klode <jak@debian.org>2009-03-08 18:13:48 +0100
committerJulian Andres Klode <jak@debian.org>2009-03-08 18:13:48 +0100
commit5afbd53a5743fe37947d0ebd282111addd325615 (patch)
tree614bd50dfa49413f664786846055981858178098 /apt/progress
parent2f5a96fb610ef9cb10cb2042a86fea34d8a48904 (diff)
downloadpython-apt-5afbd53a5743fe37947d0ebd282111addd325615.tar.gz
* apt/progress/: New package, replaces apt.progress and apt.gtk
- apt/progress/gtk2.py: Moved here from apt/gtk/widgets.py - apt/progress/__init__.py: Move here from apt/progress.py
Diffstat (limited to 'apt/progress')
-rw-r--r--apt/progress/__init__.py356
-rw-r--r--apt/progress/gtk2.py443
2 files changed, 799 insertions, 0 deletions
diff --git a/apt/progress/__init__.py b/apt/progress/__init__.py
new file mode 100644
index 00000000..51eb2426
--- /dev/null
+++ b/apt/progress/__init__.py
@@ -0,0 +1,356 @@
+# Progress.py - progress reporting classes
+#
+# Copyright (c) 2005 Canonical
+#
+# Author: Michael Vogt <michael.vogt@ubuntu.com>
+#
+# 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
+"""progress reporting classes.
+
+This module provides classes for progress reporting. They can be used with
+e.g., for reporting progress on the cache opening process, the cache update
+progress, or the package install progress.
+"""
+
+import errno
+import fcntl
+import os
+import re
+import select
+import sys
+
+import apt_pkg
+
+
+__all__ = ('CdromProgress', 'DpkgInstallProgress', 'DumbInstallProgress',
+ 'FetchProgress', 'InstallProgress', 'OpProgress', 'OpTextProgress',
+ 'TextFetchProgress')
+
+
+class OpProgress(object):
+ """Abstract class to implement reporting on cache opening.
+
+ Subclass this class to implement simple Operation progress reporting.
+ """
+
+ def __init__(self):
+ self.op = None
+ self.subOp = None
+
+ def update(self, percent):
+ """Called periodically to update the user interface."""
+
+ def done(self):
+ """Called once an operation has been completed."""
+
+
+class OpTextProgress(OpProgress):
+ """A simple text based cache open reporting class."""
+
+ def __init__(self):
+ OpProgress.__init__(self)
+
+ def update(self, percent):
+ """Called periodically to update the user interface."""
+ sys.stdout.write("\r%s: %.2i " % (self.subOp, percent))
+ sys.stdout.flush()
+
+ def done(self):
+ """Called once an operation has been completed."""
+ sys.stdout.write("\r%s: Done\n" % self.op)
+
+
+class FetchProgress(object):
+ """Report the download/fetching progress.
+
+ Subclass this class to implement fetch progress reporting
+ """
+
+ # download status constants
+ dlDone = 0
+ dlQueued = 1
+ dlFailed = 2
+ dlHit = 3
+ dlIgnored = 4
+ dlStatusStr = {dlDone: "Done",
+ dlQueued: "Queued",
+ dlFailed: "Failed",
+ dlHit: "Hit",
+ dlIgnored: "Ignored"}
+
+ def __init__(self):
+ self.eta = 0.0
+ self.percent = 0.0
+ # Make checking easier
+ self.currentBytes = 0
+ self.currentItems = 0
+ self.totalBytes = 0
+ self.totalItems = 0
+ self.currentCPS = 0
+
+ def start(self):
+ """Called when the fetching starts."""
+
+ def stop(self):
+ """Called when all files have been fetched."""
+
+ def updateStatus(self, uri, descr, shortDescr, status):
+ """Called when the status of an item changes.
+
+ This happens eg. when the downloads fails or is completed.
+ """
+
+ def pulse(self):
+ """Called periodically to update the user interface.
+
+ Return True to continue or False to cancel.
+ """
+ self.percent = (((self.currentBytes + self.currentItems) * 100.0) /
+ float(self.totalBytes + self.totalItems))
+ if self.currentCPS > 0:
+ self.eta = ((self.totalBytes - self.currentBytes) /
+ float(self.currentCPS))
+ return True
+
+ def mediaChange(self, medium, drive):
+ """react to media change events."""
+
+
+class TextFetchProgress(FetchProgress):
+ """ Ready to use progress object for terminal windows """
+
+ def __init__(self):
+ FetchProgress.__init__(self)
+ self.items = {}
+
+ def updateStatus(self, uri, descr, shortDescr, status):
+ """Called when the status of an item changes.
+
+ This happens eg. when the downloads fails or is completed.
+ """
+ if status != self.dlQueued:
+ print "\r%s %s" % (self.dlStatusStr[status], descr)
+ self.items[uri] = status
+
+ def pulse(self):
+ """Called periodically to update the user interface.
+
+ Return True to continue or False to cancel.
+ """
+ FetchProgress.pulse(self)
+ if self.currentCPS > 0:
+ s = "[%2.f%%] %sB/s %s" % (self.percent,
+ apt_pkg.SizeToStr(int(self.currentCPS)),
+ apt_pkg.TimeToStr(int(self.eta)))
+ else:
+ s = "%2.f%% [Working]" % (self.percent)
+ print "\r%s" % (s),
+ sys.stdout.flush()
+ return True
+
+ def stop(self):
+ """Called when all files have been fetched."""
+ print "\rDone downloading "
+
+ def mediaChange(self, medium, drive):
+ """react to media change events."""
+ print ("Media change: please insert the disc labeled "
+ "'%s' in the drive '%s' and press enter") % (medium, drive)
+
+ return raw_input() not in ('c', 'C')
+
+
+class DumbInstallProgress(object):
+ """Report the install progress.
+
+ Subclass this class to implement install progress reporting.
+ """
+
+ def startUpdate(self):
+ """Start update."""
+
+ def run(self, pm):
+ """Start installation."""
+ return pm.DoInstall()
+
+ def finishUpdate(self):
+ """Called when update has finished."""
+
+ def updateInterface(self):
+ """Called periodically to update the user interface"""
+
+
+class InstallProgress(DumbInstallProgress):
+ """An InstallProgress that is pretty useful.
+
+ It supports the attributes 'percent' 'status' and callbacks for the dpkg
+ errors and conffiles and status changes.
+ """
+
+ def __init__(self):
+ DumbInstallProgress.__init__(self)
+ self.selectTimeout = 0.1
+ (read, write) = os.pipe()
+ self.writefd = write
+ self.statusfd = os.fdopen(read, "r")
+ fcntl.fcntl(self.statusfd.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
+ self.read = ""
+ self.percent = 0.0
+ self.status = ""
+
+ def error(self, pkg, errormsg):
+ """Called when a error is detected during the install."""
+
+ def conffile(self, current, new):
+ """Called when a conffile question from dpkg is detected."""
+
+ def statusChange(self, pkg, percent, status):
+ """Called when the status changed."""
+
+ def updateInterface(self):
+ """Called periodically to update the interface."""
+ if self.statusfd is None:
+ return
+ try:
+ while not self.read.endswith("\n"):
+ self.read += os.read(self.statusfd.fileno(), 1)
+ except OSError, (errno_, errstr):
+ # resource temporarly unavailable is ignored
+ if errno_ != errno.EAGAIN and errno_ != errno.EWOULDBLOCK:
+ print errstr
+ if not self.read.endswith("\n"):
+ return
+
+ s = self.read
+ #print s
+ try:
+ (status, pkg, percent, status_str) = s.split(":", 3)
+ except ValueError:
+ # silently ignore lines that can't be parsed
+ self.read = ""
+ return
+ #print "percent: %s %s" % (pkg, float(percent)/100.0)
+ if status == "pmerror":
+ self.error(pkg, status_str)
+ elif status == "pmconffile":
+ # we get a string like this:
+ # 'current-conffile' 'new-conffile' useredited distedited
+ match = re.match("\s*\'(.*)\'\s*\'(.*)\'.*", status_str)
+ if match:
+ self.conffile(match.group(1), match.group(2))
+ elif status == "pmstatus":
+ if float(percent) != self.percent or status_str != self.status:
+ self.statusChange(pkg, float(percent),
+ status_str.strip())
+ self.percent = float(percent)
+ self.status = status_str.strip()
+ self.read = ""
+
+ def fork(self):
+ """Fork."""
+ return os.fork()
+
+ def waitChild(self):
+ """Wait for child progress to exit."""
+ while True:
+ select.select([self.statusfd], [], [], self.selectTimeout)
+ self.updateInterface()
+ (pid, res) = os.waitpid(self.child_pid, os.WNOHANG)
+ if pid == self.child_pid:
+ break
+ return res
+
+ def run(self, pm):
+ """Start installing."""
+ pid = self.fork()
+ if pid == 0:
+ # child
+ res = pm.DoInstall(self.writefd)
+ os._exit(res)
+ self.child_pid = pid
+ res = self.waitChild()
+ return os.WEXITSTATUS(res)
+
+
+class CdromProgress(object):
+ """Report the cdrom add progress.
+
+ Subclass this class to implement cdrom add progress reporting.
+ """
+
+ def __init__(self):
+ pass
+
+ def update(self, text, step):
+ """Called periodically to update the user interface."""
+
+ def askCdromName(self):
+ """Called to ask for the name of the cdrom."""
+
+ def changeCdrom(self):
+ """Called to ask for the cdrom to be changed."""
+
+
+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.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(os.WEXITSTATUS(res))
+ self.child_pid = pid
+ res = self.waitChild()
+ return res
+
+ def updateInterface(self):
+ """Process status messages from dpkg."""
+ if self.statusfd is None:
+ return
+ while True:
+ try:
+ self.read += os.read(self.statusfd.fileno(), 1)
+ except OSError, (errno_, errstr):
+ # resource temporarly unavailable is ignored
+ if errno_ != 11:
+ print errstr
+ break
+ if not self.read.endswith("\n"):
+ continue
+
+ statusl = self.read.split(":")
+ if len(statusl) < 3:
+ print "got garbage from dpkg: '%s'" % self.read
+ self.read = ""
+ break
+ status = statusl[2].strip()
+ #print status
+ 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.match("\s*\'(.*)\'\s*\'(.*)\'.*", statusl[3])
+ if match:
+ self.conffile(match.group(1), match.group(2))
+ else:
+ self.status = status
+ self.read = ""
diff --git a/apt/progress/gtk2.py b/apt/progress/gtk2.py
new file mode 100644
index 00000000..435265d4
--- /dev/null
+++ b/apt/progress/gtk2.py
@@ -0,0 +1,443 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2004-2005 Canonical
+#
+# Authors: Michael Vogt <michael.vogt@ubuntu.com>
+# Sebastian Heinlein <glatzor@ubuntu.com>
+# Julian Andres Klode <jak@debian.org>
+#
+# 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
+"""GObject-powered progress classes and a GTK+ status widget."""
+
+from gettext import gettext as _
+import os
+import time
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+try:
+ import glib
+except ImportError:
+ import gobject as glib
+import gobject
+import pango
+import vte
+
+import apt
+import apt_pkg
+
+
+def mksig(params=(), run=gobject.SIGNAL_RUN_FIRST, rettype=gobject.TYPE_NONE):
+ """Simplified Create a gobject signal.
+
+ This allows us to write signals easier, because we just need to define the
+ type of the parameters (in most cases).
+
+ ``params`` is a tuple which defines the types of the arguments.
+ """
+ return (run, rettype, params)
+
+
+class GOpProgress(gobject.GObject, apt.progress.OpProgress):
+ """Operation progress with GObject signals.
+
+ Signals:
+
+ * status-changed(str: operation, int: percent)
+ * status-started() - Not Implemented yet
+ * status-finished()
+
+ """
+
+ __gsignals__ = {"status-changed": mksig((str, int)),
+ "status-started": mksig(),
+ "status-finished": mksig()}
+
+ def __init__(self):
+ apt.progress.OpProgress.__init__(self)
+ gobject.GObject.__init__(self)
+ self._context = glib.main_context_default()
+
+ def update(self, percent):
+ """Called to update the percentage done"""
+ self.emit("status-changed", self.op, percent)
+ while self._context.pending():
+ self._context.iteration()
+
+ def done(self):
+ """Called when all operation have finished."""
+ self.emit("status-finished")
+
+
+class GInstallProgress(gobject.GObject, apt.progress.InstallProgress):
+ """Installation progress with GObject signals.
+
+ Signals:
+
+ * status-changed(str: status, int: percent)
+ * status-started()
+ * status-finished()
+ * status-timeout()
+ * status-error()
+ * status-conffile()
+
+ """
+ # Seconds until a maintainer script will be regarded as hanging
+ INSTALL_TIMEOUT = 5 * 60
+
+ __gsignals__ = {"status-changed": mksig((str, int)),
+ "status-started": mksig(),
+ "status-timeout": mksig(),
+ "status-error": mksig(),
+ "status-conffile": mksig(),
+ "status-finished": mksig()}
+
+ 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"]
+ self._context = glib.main_context_default()
+
+ def childExited(self, term, pid, status):
+ """Called when a child process exits"""
+ self.apt_status = os.WEXITSTATUS(status)
+ self.finished = True
+
+ def error(self, pkg, errormsg):
+ """Called when an error happens.
+
+ Emits: status-error()
+ """
+ self.emit("status-error")
+
+ def conffile(self, current, new):
+ """Called during conffile.
+
+ Emits: status-conffile()
+ """
+ self.emit("status-conffile")
+
+ def startUpdate(self):
+ """Called when the update starts.
+
+ Emits: status-started()
+ """
+ self.emit("status-started")
+
+ def finishUpdate(self):
+ """Called when the update finished.
+
+ Emits: status-finished()
+ """
+ self.emit("status-finished")
+
+ def statusChange(self, pkg, percent, status):
+ """Called when the status changed.
+
+ Emits: status-changed(status, percent)
+ """
+ self.time_last_update = time.time()
+ self.emit("status-changed", status, percent)
+
+ def updateInterface(self):
+ """Called periodically to update the interface.
+
+ Emits: status-timeout() [When a timeout happens]
+ """
+ apt.progress.InstallProgress.updateInterface(self)
+ while self._context.pending():
+ self._context.iteration()
+ if self.time_last_update + self.INSTALL_TIMEOUT < time.time():
+ self.emit("status-timeout")
+
+ def fork(self):
+ """Fork the process."""
+ return self.term.forkpty(envv=self.env)
+
+ def waitChild(self):
+ """Wait for the child process to exit."""
+ while not self.finished:
+ self.updateInterface()
+ return self.apt_status
+
+
+class GDpkgInstallProgress(apt.progress.DpkgInstallProgress, GInstallProgress):
+ """An InstallProgress for local installations.
+
+ Signals:
+
+ * status-changed(str: status, int: percent)
+ * status-started() - Not Implemented yet
+ * status-finished()
+ * status-timeout() - When the maintainer script hangs
+ * status-error() - When an error happens
+ * status-conffile() - On Conffile
+ """
+
+ def run(self, debfile):
+ """Install the given package."""
+ apt.progress.DpkgInstallProgress.run(self, debfile)
+
+ def updateInterface(self):
+ """Called periodically to update the interface.
+
+ Emits: status-timeout() [When a timeout happens]"""
+ apt.progress.DpkgInstallProgress.updateInterface(self)
+ if self.time_last_update + self.INSTALL_TIMEOUT < time.time():
+ self.emit("status-timeout")
+
+
+class GFetchProgress(gobject.GObject, apt.progress.FetchProgress):
+ """A Fetch Progress with GObject signals.
+
+ Signals:
+
+ * status-changed(str: description, int: percent)
+ * status-started()
+ * status-finished()
+ """
+
+ __gsignals__ = {"status-changed": mksig((str, int)),
+ "status-started": mksig(),
+ "status-finished": mksig()}
+
+ def __init__(self):
+ 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")
+
+ 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": apt_pkg.SizeToStr(self.currentCPS)})
+ else:
+ text = (_("Downloading file %(current)li of %(total)li") % \
+ {"current": currentItem,
+ "total": self.totalItems})
+ self.emit("status-changed", text, self.percent)
+ while self._context.pending():
+ self._context.iteration()
+ return self._continue
+
+
+class GtkAptProgress(gtk.VBox):
+ """Graphical progress for installation/fetch/operations.
+
+ This widget provides a progress bar, a terminal and a status bar for
+ showing the progress of package manipulation tasks.
+ """
+
+ 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._progressbar.set_fraction(0)
+ self._expander.set_expanded(False)
+
+ @property
+ def open(self):
+ """Return the cache opening progress handler."""
+ return self._progress_open
+
+ @property
+ def install(self):
+ """Return the install progress handler."""
+ return self._progress_install
+
+ @property
+ def dpkg_install(self):
+ """Return the install progress handler for dpkg."""
+ return self._dpkg_progress_install
+
+ @property
+ def fetch(self):
+ """Return the fetch progress handler."""
+ return self._progress_fetch
+
+ def _on_status_started(self, progress):
+ """Called when something starts."""
+ self._on_status_changed(progress, _("Starting..."), 0)
+ while gtk.events_pending():
+ gtk.main_iteration()
+
+ def _on_status_finished(self, progress):
+ """Called when something finished."""
+ self._on_status_changed(progress, _("Complete"), 100)
+ while gtk.events_pending():
+ gtk.main_iteration()
+
+ def _on_status_changed(self, progress, status, percent):
+ """Called when the status changed."""
+ self._label.set_text(status)
+ 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):
+ """Called when timeout happens."""
+ self._expander.set_expanded(True)
+ while gtk.events_pending():
+ gtk.main_iteration()
+
+ def cancel_download(self):
+ """Cancel a currently running download."""
+ self._progress_fetch.cancel()
+
+ def show_terminal(self, expanded=False):
+ """Show the expander for the terminal.
+
+ 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(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):
+ """Show the Box"""
+ gtk.HBox.show(self)
+ self._label.show()
+ self._progressbar.show()
+ while gtk.events_pending():
+ gtk.main_iteration()
+
+
+def _test():
+ """Test function"""
+ import sys
+
+ from apt.debfile import DebPackage
+
+ 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.open)
+ pkg = cache["xterm"]
+ if pkg.isInstalled:
+ pkg.markDelete()
+ else:
+ pkg.markInstall()
+ apt_progress.show_terminal(True)
+ try:
+ cache.commit(apt_progress.fetch, apt_progress.install)
+ except Exception, exc:
+ print >> sys.stderr, "Exception happened:", exc
+ if len(sys.argv) > 1:
+ deb = DebPackage(sys.argv[1], cache)
+ deb.install(apt_progress.dpkg_install)
+ gtk.main()
+
+
+if __name__ == "__main__":
+ _test()
+
+# vim: ts=4 et sts=4