summaryrefslogtreecommitdiff
path: root/apt/progress.py
diff options
context:
space:
mode:
Diffstat (limited to 'apt/progress.py')
-rw-r--r--apt/progress.py376
1 files changed, 216 insertions, 160 deletions
diff --git a/apt/progress.py b/apt/progress.py
index a8ab76b6..51eb2426 100644
--- a/apt/progress.py
+++ b/apt/progress.py
@@ -1,61 +1,82 @@
# 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
+#
+# 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.
-import sys
+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 fcntl
-import string
-from errno import *
import select
+import sys
+
import apt_pkg
-import apt
+
+__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
+ """Abstract class to implement reporting on cache opening.
+
+ Subclass this class to implement simple Operation progress reporting.
"""
+
def __init__(self):
- pass
+ self.op = None
+ self.subOp = None
+
def update(self, percent):
- pass
+ """Called periodically to update the user interface."""
+
def done(self):
- pass
+ """Called once an operation has been completed."""
+
class OpTextProgress(OpProgress):
- """ A simple text based cache open reporting class """
+ """A simple text based cache open reporting class."""
+
def __init__(self):
OpProgress.__init__(self)
+
def update(self, percent):
- sys.stdout.write("\r%s: %.2i " % (self.subOp,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
+ """Report the download/fetching progress.
+
+ Subclass this class to implement fetch progress reporting
"""
# download status constants
@@ -64,46 +85,71 @@ class FetchProgress(object):
dlFailed = 2
dlHit = 3
dlIgnored = 4
- dlStatusStr = {dlDone : "Done",
- dlQueued : "Queued",
- dlFailed : "Failed",
- dlHit : "Hit",
- dlIgnored : "Ignored"}
-
+ dlStatusStr = {dlDone: "Done",
+ dlQueued: "Queued",
+ dlFailed: "Failed",
+ dlHit: "Hit",
+ dlIgnored: "Ignored"}
+
def __init__(self):
self.eta = 0.0
self.percent = 0.0
- pass
-
+ # Make checking easier
+ self.currentBytes = 0
+ self.currentItems = 0
+ self.totalBytes = 0
+ self.totalItems = 0
+ self.currentCPS = 0
+
def start(self):
- pass
-
+ """Called when the fetching starts."""
+
def stop(self):
- pass
-
+ """Called when all files have been fetched."""
+
def updateStatus(self, uri, descr, shortDescr, status):
- pass
+ """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 gui), importend to
- return True to continue or False to cancel
+ """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)
+ 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)
+ self.eta = ((self.totalBytes - self.currentBytes) /
+ float(self.currentCPS))
return True
+
def mediaChange(self, medium, drive):
- pass
+ """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,
@@ -114,102 +160,121 @@ class TextFetchProgress(FetchProgress):
print "\r%s" % (s),
sys.stdout.flush()
return True
+
def stop(self):
- print "\rDone downloading "
+ """Called when all files have been fetched."""
+ print "\rDone downloading "
+
def mediaChange(self, medium, drive):
- """ react to media change events """
- res = True;
- print "Media change: please insert the disc labeled \
- '%s' in the drive '%s' and press enter" % (medium,drive)
- s = sys.stdin.readline()
- if(s == 'c' or s == 'C'):
- res = false;
- return res
+ """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
+ """Report the install progress.
+
+ Subclass this class to implement install progress reporting.
"""
- def __init__(self):
- pass
+
def startUpdate(self):
- pass
+ """Start update."""
+
def run(self, pm):
+ """Start installation."""
return pm.DoInstall()
+
def finishUpdate(self):
- pass
+ """Called when update has finished."""
+
def updateInterface(self):
- pass
+ """Called periodically to update the user interface"""
+
class InstallProgress(DumbInstallProgress):
- """ A InstallProgress that is pretty useful.
- It supports the attributes 'percent' 'status' and callbacks
- for the dpkg errors and conffiles and status changes
- """
+ """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.writefd = write
self.statusfd = os.fdopen(read, "r")
- fcntl.fcntl(self.statusfd.fileno(), fcntl.F_SETFL,os.O_NONBLOCK)
+ 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 "
- pass
- def conffile(self,current,new):
- " called when a conffile question from dpkg is detected "
- pass
+ """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 "
- pass
+ """Called when the status changed."""
+
def updateInterface(self):
- if self.statusfd != None:
- 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 != EAGAIN and errnor != EWOULDBLOCK:
- print errstr
- if self.read.endswith("\n"):
- s = self.read
- #print s
- try:
- (status, pkg, percent, status_str) = string.split(s, ":",3)
- except ValueError, e:
- # 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.compile("\s*\'(.*)\'\s*\'(.*)\'.*").match(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 = string.strip(status_str)
- self.read = ""
+ """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)
+ select.select([self.statusfd], [], [], self.selectTimeout)
self.updateInterface()
- (pid, res) = os.waitpid(self.child_pid,os.WNOHANG)
+ (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
@@ -219,29 +284,31 @@ class InstallProgress(DumbInstallProgress):
res = self.waitChild()
return os.WEXITSTATUS(res)
-class CdromProgress:
- """ Report the cdrom add progress
- Subclass this class to implement cdrom add progress reporting
+
+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):
- """ update is called regularly so that the gui can be redrawn """
- pass
+ """Called periodically to update the user interface."""
+
def askCdromName(self):
- pass
+ """Called to ask for the name of the cdrom."""
+
def changeCdrom(self):
- pass
+ """Called to ask for the cdrom to be changed."""
class DpkgInstallProgress(InstallProgress):
- """
- Progress handler for a local Debian package installation
- """
+ """Progress handler for a local Debian package installation."""
+
def run(self, debfile):
- """
- Start installing the given Debian package
- """
+ """Start installing the given Debian package."""
self.debfile = debfile
self.debname = os.path.basename(debfile).split("_")[0]
pid = self.fork()
@@ -255,46 +322,35 @@ class DpkgInstallProgress(InstallProgress):
return res
def updateInterface(self):
- """
- Process status messages from dpkg
- """
- if self.statusfd != None:
- 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 self.read.endswith("\n"):
- statusl = string.split(self.read, ":")
- if len(statusl) < 3:
- print "got garbage from dpkg: '%s'" % 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.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__":
- import apt_pkg
- apt_pkg.init()
- progress = OpTextProgress()
- cache = apt_pkg.GetCache(progress)
- depcache = apt_pkg.GetDepCache(cache)
- depcache.Init(progress)
-
- fprogress = TextFetchProgress()
- cache.Update(fprogress)
+ """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 = ""