summaryrefslogtreecommitdiff
path: root/apt/progress
diff options
context:
space:
mode:
Diffstat (limited to 'apt/progress')
-rw-r--r--apt/progress/__init__.py420
-rw-r--r--apt/progress/base.py302
-rw-r--r--apt/progress/gtk2.py239
-rw-r--r--apt/progress/old.py205
-rw-r--r--apt/progress/text.py261
5 files changed, 951 insertions, 476 deletions
diff --git a/apt/progress/__init__.py b/apt/progress/__init__.py
index 8694de77..10c11021 100644
--- a/apt/progress/__init__.py
+++ b/apt/progress/__init__.py
@@ -1,405 +1,35 @@
-# progress.py - progress reporting classes
+# apt/progress/__init__.py - Initialization file for apt.progress.
#
-# Copyright (c) 2005-2009 Canonical
+# Copyright (c) 2009 Julian Andres Klode <jak@debian.org>
#
-# 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 free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; either version 2 of the
-# License, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-# USA
-"""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.
+# 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.
+
+This package provides progress reporting for the python-apt package. The module
+'base' provides classes with no output, the module 'gtk2' provides classes for
+GTK+ applications, and the module 'text' provides classes for terminals, etc.
"""
-
-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 update_status_full(self, uri, descr, short_descr, status, file_size,
- partial_size):
- """Called when the status of an item changes.
-
- This happens eg. when the downloads fails or is completed. This
- version include information on current filesize and partial size
- """
-
- 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 pulse_items(self, items):
- """Called periodically to update the user interface.
- This function includes details about the items being fetched
- 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"):
- r = os.read(self.statusfd.fileno(), 1)
- if not r:
- return
- self.read += r
- 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.
-
- The return values is the full status returned from os.waitpid()
- (not only the return code).
- """
- while True:
- try:
- select.select([self.statusfd], [], [], self.selectTimeout)
- except select.error, (errno_, errstr):
- if errno_ != errno.EINTR:
- raise
- self.updateInterface()
- try:
- (pid, res) = os.waitpid(self.child_pid, os.WNOHANG)
- if pid == self.child_pid:
- break
- except OSError, (errno_, errstr):
- if errno_ == errno.ECHILD:
- break
- if errno_ != errno.EINTR:
- raise
- return res
-
- def run(self, pm):
- """Start installing.
-
- Returns the PackageManager status:
- (pm.ResultCompleted, pm.ResultFailed, pm.ResultIncomplete)
- """
- pid = self.fork()
- if pid == 0:
- # pm.DoInstall might raise a exception,
- # when this happens, we need to catch
- # it, otherwise os._exit() is not run
- # and the execution continues in the
- # parent code leading to very confusing bugs
- try:
- res = pm.DoInstall(self.writefd)
- except Exception, e:
- os._exit(pm.ResultFailed)
- 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, debfile))
- os._exit(os.WEXITSTATUS(res))
- self.child_pid = pid
- res = self.waitChild()
- return res
+__all__ = []
- 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
- pkg_name = statusl[1].strip()
- status = statusl[2].strip()
- #print status
- if status == "error":
- self.error(pkg_name, 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 = ""
+if apt_pkg._COMPAT_0_7:
+ from apt.progress.old import (CdromProgress, DpkgInstallProgress,
+ DumbInstallProgress, FetchProgress,
+ InstallProgress, OpProgress,
+ OpTextProgress, TextFetchProgress)
diff --git a/apt/progress/base.py b/apt/progress/base.py
new file mode 100644
index 00000000..6636cccc
--- /dev/null
+++ b/apt/progress/base.py
@@ -0,0 +1,302 @@
+# apt/progress/base.py - Base classes for progress reporting.
+#
+# Copyright (C) 2009 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.
+#
+# 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
+"""Base classes for progress reporting.
+
+Custom progress classes should inherit from these classes. They can also be
+used as dummy progress classes which simply do nothing.
+"""
+import errno
+import fcntl
+import os
+import re
+import select
+
+import apt_pkg
+
+__all__ = ['AcquireProgress', 'CdromProgress', 'InstallProgress', 'OpProgress']
+
+
+class AcquireProgress(object):
+ """Monitor object for downloads controlled by the Acquire class.
+
+ This is an mostly abstract class. You should subclass it and implement the
+ methods to get something useful.
+ """
+
+ current_bytes = current_cps = fetched_bytes = last_bytes = total_bytes \
+ = 0.0
+ current_items = elapsed_time = total_items = 0
+
+ def done(self, item):
+ """Invoked when an item is successfully and completely fetched."""
+
+ def fail(self, item):
+ """Invoked when an item could not be fetched."""
+
+ def fetch(self, item):
+ """Invoked when some of the item's data is fetched."""
+
+ def ims_hit(self, item):
+ """Invoked when an item is confirmed to be up-to-date.
+
+ Invoked when an item is confirmed to be up-to-date. For instance,
+ when an HTTP download is informed that the file on the server was
+ not modified.
+ """
+
+ def media_change(self, media, drive):
+ """Prompt the user to change the inserted removable media.
+
+ The parameter 'media' decribes the name of the the media type that
+ should be changed, whereas the parameter 'drive' should be the
+ identifying name of the drive whose media should be changed.
+
+ This method should not return until the user has confirmed to the user
+ interface that the media change is complete. It must return True if
+ the user confirms the media change, or False to cancel it.
+ """
+ return False
+
+ def pulse(self, owner):
+ """Periodically invoked while the Acquire process is underway.
+
+ This method gets invoked while the Acquire progress given by the
+ parameter 'owner' is underway. It should display information about
+ the current state.
+
+ This function returns a boolean value indicating whether the
+ acquisition should be continued (True) or cancelled (False).
+ """
+ return True
+
+ def start(self):
+ """Invoked when the Acquire process starts running."""
+ # Reset all our values.
+ self.current_bytes = 0.0
+ self.current_cps = 0.0
+ self.current_items = 0
+ self.elapsed_time = 0
+ self.fetched_bytes = 0.0
+ self.last_bytes = 0.0
+ self.total_bytes = 0.0
+ self.total_items = 0
+
+ def stop(self):
+ """Invoked when the Acquire process stops running."""
+
+
+class CdromProgress(object):
+ """Base class for reporting the progress of adding a cdrom.
+
+ Can be used with apt_pkg.Cdrom to produce an utility like apt-cdrom. The
+ attribute 'total_steps' defines the total number of steps and can be used
+ in update() to display the current progress.
+ """
+
+ total_steps = 0
+
+ def ask_cdrom_name(self):
+ """Ask for the name of the cdrom.
+
+ If a name has been provided, return it. Otherwise, return None to
+ cancel the operation.
+ """
+
+ def change_cdrom(self):
+ """Ask for the CD-ROM to be changed.
+
+ Return True once the cdrom has been changed or False to cancel the
+ operation.
+ """
+
+ def update(self, text, current):
+ """Periodically invoked to update the interface.
+
+ The string 'text' defines the text which should be displayed. The
+ integer 'current' defines the number of completed steps.
+ """
+
+
+class InstallProgress(object):
+ """Class to report the progress of installing packages."""
+
+ percent, select_timeout, status = 0.0, 0.1, ""
+
+ def __init__(self):
+ (read, write) = os.pipe()
+ self.writefd = os.fdopen(write, "w")
+ self.statusfd = os.fdopen(read, "r")
+ fcntl.fcntl(self.statusfd, fcntl.F_SETFL, os.O_NONBLOCK)
+
+ def start_update(self):
+ """(Abstract) Start update."""
+
+ def finish_update(self):
+ """(Abstract) Called when update has finished."""
+
+ def error(self, pkg, errormsg):
+ """(Abstract) Called when a error is detected during the install."""
+
+ def conffile(self, current, new):
+ """(Abstract) Called when a conffile question from dpkg is detected."""
+
+ def status_change(self, pkg, percent, status):
+ """(Abstract) Called when the APT status changed."""
+
+ def dpkg_status_change(self, pkg, status):
+ """(Abstract) Called when the dpkg status changed."""
+
+ def processing(self, pkg, stage):
+ """(Abstract) Sent just before a processing stage starts.
+
+ The parameter 'stage' is one of "upgrade", "install"
+ (both sent before unpacking), "configure", "trigproc", "remove",
+ "purge". This method is used for dpkg only.
+ """
+
+ def run(self, obj):
+ """Install using the object 'obj'.
+
+ This functions runs install actions. The parameter 'obj' may either
+ be a PackageManager object in which case its do_install() method is
+ called or the path to a deb file.
+
+ If the object is a PackageManager, the functions returns the result
+ of calling its do_install() method. Otherwise, the function returns
+ the exit status of dpkg. In both cases, 0 means that there were no
+ problems.
+ """
+ pid = self.fork()
+ if pid == 0:
+ # pm.do_install might raise a exception,
+ # when this happens, we need to catch
+ # it, otherwise os._exit() is not run
+ # and the execution continues in the
+ # parent code leading to very confusing bugs
+ try:
+ os._exit(obj.do_install(self.writefd.fileno()))
+ except AttributeError:
+ os._exit(os.spawnlp(os.P_WAIT, "dpkg", "dpkg", "--status-fd",
+ str(self.writefd.fileno()), "-i", obj))
+ except Exception:
+ os._exit(apt_pkg.PackageManager.RESULT_FAILED)
+
+ self.child_pid = pid
+ res = self.wait_child()
+ return os.WEXITSTATUS(res)
+
+ def fork(self):
+ """Fork."""
+ return os.fork()
+
+ def update_interface(self):
+ """Update the interface."""
+ try:
+ line = self.statusfd.readline()
+ except IOError, err:
+ # resource temporarly unavailable is ignored
+ if err.errno != errno.EAGAIN and err.errno != errno.EWOULDBLOCK:
+ print err.strerror
+ return
+
+ pkgname = status = status_str = percent = base = ""
+
+ if line.startswith('pm'):
+ try:
+ (status, pkgname, percent, status_str) = line.split(":", 3)
+ except ValueError:
+ # silently ignore lines that can't be parsed
+ self.read = ""
+ return
+ elif line.startswith('status'):
+ try:
+ (base, pkgname, status, status_str) = line.split(":", 3)
+ except ValueError:
+ (base, pkgname, status) = line.split(":", 2)
+ elif line.startswith('processing'):
+ (status, status_str, pkgname) = line.split(":", 2)
+ self.processing(pkgname.strip(), status_str.strip())
+
+ # Always strip the status message
+ pkgname = pkgname.strip()
+ status_str = status_str.strip()
+ status = status.strip()
+
+ if status == 'pmerror' or status == 'error':
+ self.error(pkgname, status_str)
+ elif status == 'conffile-prompt' or status == 'pmconffile':
+ match = re.match("\s*\'(.*)\'\s*\'(.*)\'.*", status_str)
+ if match:
+ self.conffile(match.group(1), match.group(2))
+ elif status == "pmstatus":
+ # FIXME: Float comparison
+ if float(percent) != self.percent or status_str != self.status:
+ self.status_change(pkgname, float(percent), status_str.strip())
+ self.percent = float(percent)
+ self.status = status_str.strip()
+ elif base == "status":
+ self.dpkg_status_change(pkgname, status)
+
+ def wait_child(self):
+ """Wait for child progress to exit.
+
+ This method is responsible for calling update_interface() from time to
+ time. It exits once the child has exited. The return values is the
+ full status returned from os.waitpid() (not only the return code).
+ """
+ (pid, res) = (0, 0)
+ while True:
+ try:
+ select.select([self.statusfd], [], [], self.select_timeout)
+ except select.error, (errno_, errstr):
+ if errno_ != errno.EINTR:
+ raise
+
+ self.update_interface()
+ try:
+ (pid, res) = os.waitpid(self.child_pid, os.WNOHANG)
+ if pid == self.child_pid:
+ break
+ except OSError, err:
+ if err.errno == errno.ECHILD:
+ break
+ if err.errno != errno.EINTR:
+ raise
+
+ return res
+
+
+class OpProgress(object):
+ """Monitor objects for operations.
+
+ Display the progress of operations such as opening the cache."""
+
+ major_change, op, percent, subop = False, "", 0.0, ""
+
+ def update(self, percent=None):
+ """Called periodically to update the user interface.
+
+ You may use the optional argument 'percent' to set the attribute
+ 'percent' in this call.
+ """
+ if percent is not None:
+ self.percent = percent
+
+ def done(self):
+ """Called once an operation has been completed."""
diff --git a/apt/progress/gtk2.py b/apt/progress/gtk2.py
index f872e34f..29e730a3 100644
--- a/apt/progress/gtk2.py
+++ b/apt/progress/gtk2.py
@@ -11,7 +11,7 @@
# 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,
+# 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.
@@ -22,7 +22,6 @@
# USA
"""GObject-powered progress classes and a GTK+ status widget."""
-from gettext import gettext as _
import os
import time
@@ -37,8 +36,17 @@ import gobject
import pango
import vte
-import apt
import apt_pkg
+from apt_pkg import gettext as _
+from apt.deprecation import function_deprecated_by, AttributeDeprecatedBy
+from apt.progress import base
+
+if apt_pkg._COMPAT_0_7:
+ from apt.progress import old
+
+
+__all__ = ['GAcquireProgress', 'GInstallProgress', 'GOpProgress',
+ 'GtkAptProgress']
def mksig(params=(), run=gobject.SIGNAL_RUN_FIRST, rettype=gobject.TYPE_NONE):
@@ -52,7 +60,7 @@ def mksig(params=(), run=gobject.SIGNAL_RUN_FIRST, rettype=gobject.TYPE_NONE):
return (run, rettype, params)
-class GOpProgress(gobject.GObject, apt.progress.OpProgress):
+class GOpProgress(gobject.GObject, base.OpProgress):
"""Operation progress with GObject signals.
Signals:
@@ -68,22 +76,28 @@ class GOpProgress(gobject.GObject, apt.progress.OpProgress):
"status-finished": mksig()}
def __init__(self):
- apt.progress.OpProgress.__init__(self)
+ base.OpProgress.__init__(self)
gobject.GObject.__init__(self)
self._context = glib.main_context_default()
- def update(self, percent):
+ def update(self, percent=None):
"""Called to update the percentage done"""
- self.emit("status-changed", self.op, percent)
+ base.OpProgress.update(self, percent)
+ self.emit("status-changed", self.op, self.percent)
while self._context.pending():
self._context.iteration()
def done(self):
"""Called when all operation have finished."""
+ base.OpProgress.done(self)
self.emit("status-finished")
+ if apt_pkg._COMPAT_0_7:
+ subOp = AttributeDeprecatedBy('subop')
+ Op = AttributeDeprecatedBy('op')
-class GInstallProgress(gobject.GObject, apt.progress.InstallProgress):
+
+class GInstallProgress(gobject.GObject, base.InstallProgress):
"""Installation progress with GObject signals.
Signals:
@@ -107,19 +121,19 @@ class GInstallProgress(gobject.GObject, apt.progress.InstallProgress):
"status-finished": mksig()}
def __init__(self, term):
- apt.progress.InstallProgress.__init__(self)
+ base.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)
+ reaper.connect("child-exited", self.child_exited)
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):
+ def child_exited(self, term, pid, status):
"""Called when a child process exits"""
self.apt_status = os.WEXITSTATUS(status)
self.finished = True
@@ -138,21 +152,31 @@ class GInstallProgress(gobject.GObject, apt.progress.InstallProgress):
"""
self.emit("status-conffile")
- def startUpdate(self):
+ def start_update(self):
"""Called when the update starts.
Emits: status-started()
"""
self.emit("status-started")
- def finishUpdate(self):
+ def run(self, obj):
+ """Run."""
+ self.finished = False
+ return base.InstallProgress.run(self, obj)
+
+ def finish_update(self):
"""Called when the update finished.
Emits: status-finished()
"""
self.emit("status-finished")
- def statusChange(self, pkg, percent, status):
+ def processing(self, pkg, stage):
+ """Called when entering a new stage in dpkg."""
+ # We have no percentage or alike, send -1 to let the bar pulse.
+ self.emit("status-changed", ("Installing %s...") % pkg, -1)
+
+ def status_change(self, pkg, percent, status):
"""Called when the status changed.
Emits: status-changed(status, percent)
@@ -160,12 +184,12 @@ class GInstallProgress(gobject.GObject, apt.progress.InstallProgress):
self.time_last_update = time.time()
self.emit("status-changed", status, percent)
- def updateInterface(self):
+ def update_interface(self):
"""Called periodically to update the interface.
Emits: status-timeout() [When a timeout happens]
"""
- apt.progress.InstallProgress.updateInterface(self)
+ base.InstallProgress.update_interface(self)
while self._context.pending():
self._context.iteration()
if self.time_last_update + self.INSTALL_TIMEOUT < time.time():
@@ -175,40 +199,25 @@ class GInstallProgress(gobject.GObject, apt.progress.InstallProgress):
"""Fork the process."""
return self.term.forkpty(envv=self.env)
- def waitChild(self):
+ def wait_child(self):
"""Wait for the child process to exit."""
while not self.finished:
- self.updateInterface()
+ self.update_interface()
return self.apt_status
+ if apt_pkg._COMPAT_0_7:
+ updateInterface = function_deprecated_by(update_interface)
+ startUpdate = function_deprecated_by(start_update)
+ finishUpdate = function_deprecated_by(finish_update)
+ statusChange = function_deprecated_by(status_change)
+ waitChild = function_deprecated_by(wait_child)
+ childExited = function_deprecated_by(child_exited)
-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")
+GDpkgInstallProgress = GInstallProgress
-class GFetchProgress(gobject.GObject, apt.progress.FetchProgress):
+class GAcquireProgress(gobject.GObject, base.AcquireProgress):
"""A Fetch Progress with GObject signals.
Signals:
@@ -216,6 +225,8 @@ class GFetchProgress(gobject.GObject, apt.progress.FetchProgress):
* status-changed(str: description, int: percent)
* status-started()
* status-finished()
+
+ DEPRECATED.
"""
__gsignals__ = {"status-changed": mksig((str, int)),
@@ -223,40 +234,98 @@ class GFetchProgress(gobject.GObject, apt.progress.FetchProgress):
"status-finished": mksig()}
def __init__(self):
- apt.progress.FetchProgress.__init__(self)
+ base.AcquireProgress.__init__(self)
gobject.GObject.__init__(self)
self._continue = True
self._context = glib.main_context_default()
def start(self):
+ base.AcquireProgress.start(self)
self.emit("status-started")
def stop(self):
+ base.AcquireProgress.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:
+ def pulse(self, owner):
+ base.AcquireProgress.pulse(self, owner)
+ current_item = self.current_items + 1
+ if current_item > self.total_items:
+ current_item = self.total_items
+ if self.current_cps > 0:
text = (_("Downloading file %(current)li of %(total)li with "
"%(speed)s/s") % \
- {"current": currentItem,
- "total": self.totalItems,
- "speed": apt_pkg.SizeToStr(self.currentCPS)})
+ {"current": current_item,
+ "total": self.total_items,
+ "speed": apt_pkg.size_to_str(self.current_cps)})
else:
text = (_("Downloading file %(current)li of %(total)li") % \
- {"current": currentItem,
- "total": self.totalItems})
- self.emit("status-changed", text, self.percent)
+ {"current": current_item,
+ "total": self.total_items})
+
+ percent = (((self.current_bytes + self.current_items) * 100.0) /
+ float(self.total_bytes + self.total_items))
+ self.emit("status-changed", text, percent)
while self._context.pending():
self._context.iteration()
return self._continue
+if apt_pkg._COMPAT_0_7:
+
+ class GFetchProgress(gobject.GObject, old.FetchProgress):
+ """A Fetch Progress with GObject signals.
+
+ Signals:
+
+ * status-changed(str: description, int: percent)
+ * status-started()
+ * status-finished()
+
+ DEPRECATED.
+ """
+
+ __gsignals__ = {"status-changed": mksig((str, int)),
+ "status-started": mksig(),
+ "status-finished": mksig()}
+
+ def __init__(self):
+ old.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):
+ old.FetchProgress.pulse(self)
+ current_item = self.currentItems + 1
+ if current_item > self.totalItems:
+ current_item = self.totalItems
+ if self.current_cps > 0:
+ text = (_("Downloading file %(current)li of %(total)li with "
+ "%(speed)s/s") % \
+ {"current": current_item,
+ "total": self.totalItems,
+ "speed": apt_pkg.size_to_str(self.currentCPS)})
+ else:
+ text = (_("Downloading file %(current)li of %(total)li") % \
+ {"current": current_item,
+ "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.
@@ -291,11 +360,15 @@ class GtkAptProgress(gtk.VBox):
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._progress_acquire = GAcquireProgress()
+ self._progress_acquire.connect("status-changed",
+ self._on_status_changed)
+ self._progress_acquire.connect("status-started",
+ self._on_status_started)
+ self._progress_acquire.connect("status-finished",
self._on_status_finished)
+
+ self._progress_fetch = None
self._progress_install = GInstallProgress(self._terminal)
self._progress_install.connect("status-changed",
self._on_status_changed)
@@ -309,19 +382,6 @@ class GtkAptProgress(gtk.VBox):
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."""
@@ -342,12 +402,27 @@ class GtkAptProgress(gtk.VBox):
@property
def dpkg_install(self):
"""Return the install progress handler for dpkg."""
- return self._dpkg_progress_install
+ return self._progress_install
+
+ if apt_pkg._COMPAT_0_7:
+
+ @property
+ def fetch(self):
+ """Return the fetch progress handler."""
+ if self._progress_fetch is None:
+ 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)
+ return self._progress_fetch
@property
- def fetch(self):
- """Return the fetch progress handler."""
- return self._progress_fetch
+ def acquire(self):
+ """Return the acquire progress handler."""
+ return self._progress_acquire
def _on_status_started(self, progress):
"""Called when something starts."""
@@ -364,7 +439,7 @@ class GtkAptProgress(gtk.VBox):
def _on_status_changed(self, progress, status, percent):
"""Called when the status changed."""
self._label.set_text(status)
- if percent is None:
+ if percent is None or percent == -1:
self._progressbar.pulse()
else:
self._progressbar.set_fraction(percent/100.0)
@@ -412,6 +487,7 @@ def _test():
"""Test function"""
import sys
+ import apt
from apt.debfile import DebPackage
win = gtk.Window()
@@ -422,18 +498,19 @@ def _test():
win.show()
cache = apt.cache.Cache(apt_progress.open)
pkg = cache["xterm"]
- if pkg.isInstalled:
- pkg.markDelete()
+ if pkg.is_installed:
+ pkg.mark_delete()
else:
- pkg.markInstall()
+ pkg.mark_install()
apt_progress.show_terminal(True)
try:
- cache.commit(apt_progress.fetch, apt_progress.install)
+ cache.commit(apt_progress.acquire, 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)
+ win.connect("destroy", gtk.main_quit)
gtk.main()
diff --git a/apt/progress/old.py b/apt/progress/old.py
new file mode 100644
index 00000000..c2d95b85
--- /dev/null
+++ b/apt/progress/old.py
@@ -0,0 +1,205 @@
+# progress.py - progress reporting classes
+#
+# Copyright (c) 2005-2009 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
+"""Deprecated progress reporting classes.
+
+This module provides classes for compatibility with python-apt 0.7. They are
+completely deprecated and should not be used anymore.
+"""
+
+
+import os
+import sys
+
+import apt_pkg
+from apt.deprecation import AttributeDeprecatedBy, function_deprecated_by
+import warnings
+from apt.progress import base, text
+
+__all__ = []
+
+
+class OpProgress(base.OpProgress):
+ """Abstract class to implement reporting on cache opening."""
+
+ subOp = AttributeDeprecatedBy('subop')
+ Op = AttributeDeprecatedBy('op')
+
+
+class OpTextProgress(OpProgress, text.OpProgress):
+ """A simple text based cache open reporting class."""
+
+
+class FetchProgress(object):
+ """Report the download/fetching progress."""
+
+ # download status constants
+ (dlDone, dlQueued, dlFailed, dlHit, dlIgnored) = range(5)
+ 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
+ warnings.warn("FetchProgress() is deprecated.", DeprecationWarning)
+
+ def start(self):
+ """Called when the fetching starts."""
+
+ def stop(self):
+ """Called when all files have been fetched."""
+
+ def updateStatus(self, uri, descr, short_descr, status):
+ """Called when the status of an item changes.
+
+ This happens eg. when the downloads fails or is completed.
+ """
+
+ def update_status_full(self, uri, descr, short_descr, status, file_size,
+ partial_size):
+ """Called when the status of an item changes.
+
+ This happens eg. when the downloads fails or is completed. This
+ version include information on current filesize and partial size
+ """
+
+ 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 pulse_items(self, items):
+ """Called periodically to update the user interface.
+ This function includes details about the items being fetched
+ 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, short_descr, 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.size_to_str(int(self.currentCPS)),
+ apt_pkg.time_to_str(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 CdromProgress(base.CdromProgress):
+ """Report the cdrom add progress.
+
+ This class has been replaced by apt_pkg.CdromProgress.
+ """
+ _basetype = base.CdromProgress
+ askCdromName = function_deprecated_by(_basetype.ask_cdrom_name)
+ changeCdrom = function_deprecated_by(_basetype.change_cdrom)
+ del _basetype
+
+
+class DumbInstallProgress(base.InstallProgress):
+ """Report the install progress.
+
+ Subclass this class to implement install progress reporting.
+ """
+
+ startUpdate = function_deprecated_by(base.InstallProgress.start_update)
+ finishUpdate = function_deprecated_by(base.InstallProgress.finish_update)
+ updateInterface = function_deprecated_by(
+ base.InstallProgress.update_interface)
+
+
+class InstallProgress(DumbInstallProgress, base.InstallProgress):
+ """An InstallProgress that is pretty useful.
+
+ It supports the attributes 'percent' 'status' and callbacks for the dpkg
+ errors and conffiles and status changes.
+ """
+
+ selectTimeout = AttributeDeprecatedBy('select_timeout')
+ statusChange = function_deprecated_by(base.InstallProgress.status_change)
+ waitChild = function_deprecated_by(base.InstallProgress.wait_child)
+
+
+class DpkgInstallProgress(InstallProgress):
+ """Progress handler for a local Debian package installation."""
+
+ def run(self, debfile):
+ """Start installing the given Debian package."""
+ # Deprecated stuff
+ self.debfile = debfile
+ self.debname = os.path.basename(debfile).split("_")[0]
+ return base.InstallProgress(self, debfile)
diff --git a/apt/progress/text.py b/apt/progress/text.py
new file mode 100644
index 00000000..5e45c1db
--- /dev/null
+++ b/apt/progress/text.py
@@ -0,0 +1,261 @@
+# Copyright (c) 2009 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.
+#
+# 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 for text interfaces."""
+import sys
+
+import apt_pkg
+from apt.progress import base
+
+__all__ = ['AcquireProgress', 'CdromProgress', 'OpProgress']
+
+
+def _(msg):
+ """Translate the message, also try apt if translation is missing."""
+ res = apt_pkg.gettext(msg)
+ if res == msg:
+ res = apt_pkg.gettext(msg, "apt")
+ return res
+
+
+class TextProgress(object):
+ """Internal Base class for text progress classes."""
+
+ def __init__(self, outfile=None):
+ self._file = outfile or sys.stdout
+ self._width = 0
+
+ def _write(self, msg, newline=True, maximize=False):
+ """Write the message on the terminal, fill remaining space."""
+ self._file.write("\r")
+ self._file.write(msg)
+
+ # Fill remaining stuff with whitespace
+ if self._width > len(msg):
+ self._file.write((self._width - len(msg)) * ' ')
+ elif maximize: # Needed for OpProgress.
+ self._width = max(self._width, len(msg))
+ if newline:
+ self._file.write("\n")
+ else:
+ #self._file.write("\r")
+ self._file.flush()
+
+
+class OpProgress(base.OpProgress, TextProgress):
+ """Operation progress reporting.
+
+ This closely resembles OpTextProgress in libapt-pkg.
+ """
+
+ def __init__(self, outfile=None):
+ TextProgress.__init__(self, outfile)
+ base.OpProgress.__init__(self)
+ self.old_op = ""
+
+ def update(self, percent=None):
+ """Called periodically to update the user interface."""
+ base.OpProgress.update(self, percent)
+ if self.major_change and self.old_op:
+ self._write(self.old_op)
+ self._write("%s... %i%%\r" % (self.op, self.percent), False, True)
+ self.old_op = self.op
+
+ def done(self):
+ """Called once an operation has been completed."""
+ base.OpProgress.done(self)
+ if self.old_op:
+ self._write(_("%c%s... Done") % ('\r', self.old_op), True, True)
+ self.old_op = ""
+
+
+class AcquireProgress(base.AcquireProgress, TextProgress):
+ """AcquireProgress for the text interface."""
+
+ def __init__(self, outfile=None):
+ TextProgress.__init__(self, outfile)
+ base.AcquireProgress.__init__(self)
+ self._signal = None
+ self._width = 80
+ self._id = 1
+
+ def start(self):
+ """Start an Acquire progress.
+
+ In this case, the function sets up a signal handler for SIGWINCH, i.e.
+ window resize signals. And it also sets id to 1.
+ """
+ base.AcquireProgress.start(self)
+ import signal
+ self._signal = signal.signal(signal.SIGWINCH, self._winch)
+ # Get the window size.
+ self._winch()
+ self._id = 1L
+
+ def _winch(self, *dummy):
+ """Signal handler for window resize signals."""
+ import fcntl
+ import termios
+ import struct
+ buf = fcntl.ioctl(self._file, termios.TIOCGWINSZ, 8 * ' ')
+ dummy, col, dummy, dummy = struct.unpack('hhhh', buf)
+ self._width = col - 1 # 1 for the cursor
+
+ def ims_hit(self, item):
+ """Called when an item is update (e.g. not modified on the server)."""
+ base.AcquireProgress.ims_hit(self, item)
+ line = _('Hit ') + item.description
+ if item.owner.filesize:
+ line += ' [%sB]' % apt_pkg.size_to_str(item.owner.filesize)
+ self._write(line)
+
+ def fail(self, item):
+ """Called when an item is failed."""
+ base.AcquireProgress.fail(self, item)
+ if item.owner.status == item.owner.STAT_DONE:
+ self._write(_("Ign ") + item.description)
+ else:
+ self._write(_("Err ") + item.description)
+ self._write(" %s" % item.owner.error_text)
+
+ def fetch(self, item):
+ """Called when some of the item's data is fetched."""
+ base.AcquireProgress.fetch(self, item)
+ # It's complete already (e.g. Hit)
+ if item.owner.complete:
+ return
+ item.owner.id = self._id
+ self._id += 1
+ line = _("Get:") + "%s %s" % (item.owner.id, item.description)
+ if item.owner.filesize:
+ line += (" [%sB]" % apt_pkg.size_to_str(item.owner.filesize))
+
+ self._write(line)
+
+ def pulse(self, owner):
+ """Periodically invoked while the Acquire process is underway.
+
+ Return False if the user asked to cancel the whole Acquire process."""
+ base.AcquireProgress.pulse(self, owner)
+ percent = (((self.current_bytes + self.current_items) * 100.0) /
+ float(self.total_bytes + self.total_items))
+
+ shown = False
+ tval = '%i%%' % percent
+
+ end = ""
+ if self.current_cps:
+ eta = int(float(self.total_bytes - self.current_bytes) /
+ self.current_cps)
+ end = " %sB/s %s" % (apt_pkg.size_to_str(self.current_cps),
+ apt_pkg.time_to_str(eta))
+
+ for worker in owner.workers:
+ val = ''
+ if not worker.current_item:
+ if worker.status:
+ val = ' [%s]' % worker.status
+ if len(tval) + len(val) + len(end) >= self._width:
+ break
+ tval += val
+ shown = True
+ continue
+ shown = True
+
+ if worker.current_item.owner.id:
+ val += " [%i %s" % (worker.current_item.owner.id,
+ worker.current_item.shortdesc)
+ else:
+ val += ' [%s' % worker.current_item.description
+ if worker.current_item.owner.mode:
+ val += ' %s' % worker.current_item.owner.mode
+
+ val += ' %sB' % apt_pkg.size_to_str(worker.current_size)
+
+ # Add the total size and percent
+ if worker.total_size and not worker.current_item.owner.complete:
+ val += "/%sB %i%%" % (apt_pkg.size_to_str(worker.total_size),
+ worker.current_size*100.0/worker.total_size)
+
+ val += ']'
+
+ if len(tval) + len(val) + len(end) >= self._width:
+ # Display as many items as screen width
+ break
+ else:
+ tval += val
+
+ if not shown:
+ tval += _(" [Working]")
+
+ if self.current_cps:
+ tval += (self._width - len(end) - len(tval)) * ' ' + end
+
+ self._write(tval, False)
+ return True
+
+ def media_change(self, medium, drive):
+ """Prompt the user to change the inserted removable media."""
+ base.AcquireProgress.media_change(self, medium, drive)
+ self._write(_("Media change: please insert the disc labeled\n"
+ " '%s'\n"
+ "in the drive '%s' and press enter\n") % (medium, drive))
+ return raw_input() not in ('c', 'C')
+
+ def stop(self):
+ """Invoked when the Acquire process stops running."""
+ base.AcquireProgress.stop(self)
+ # Trick for getting a translation from apt
+ self._write((_("Fetched %sB in %s (%sB/s)\n") % (
+ apt_pkg.size_to_str(self.fetched_bytes),
+ apt_pkg.time_to_str(self.elapsed_time),
+ apt_pkg.size_to_str(self.current_cps))).rstrip("\n"))
+
+ # Delete the signal again.
+ import signal
+ signal.signal(signal.SIGWINCH, self._signal)
+
+
+class CdromProgress(base.CdromProgress, TextProgress):
+ """Text CD-ROM progress."""
+
+ def ask_cdrom_name(self):
+ """Ask the user to provide a name for the disc."""
+ base.CdromProgress.ask_cdrom_name(self)
+ self._write(_("Please provide a name for this Disc, such as "
+ "'Debian 2.1r1 Disk 1'"), False)
+ try:
+ return raw_input(":")
+ except KeyboardInterrupt:
+ return
+
+ def update(self, text, current):
+ """Set the current progress."""
+ base.CdromProgress.update(self, text, current)
+ if text:
+ self._write(text, False)
+
+ def change_cdrom(self):
+ """Ask the user to change the CD-ROM."""
+ base.CdromProgress.change_cdrom(self)
+ self._write(_("Please insert a Disc in the drive and press enter"),
+ False)
+ try:
+ return (raw_input() == '')
+ except KeyboardInterrupt:
+ return False
+
+InstallProgress = base.InstallProgress