summaryrefslogtreecommitdiff
path: root/apt/progress/__init__.py
diff options
context:
space:
mode:
authorMichael Vogt <michael.vogt@ubuntu.com>2010-02-17 16:17:36 +0100
committerMichael Vogt <michael.vogt@ubuntu.com>2010-02-17 16:17:36 +0100
commitd1b2c0fbd34cee0ffced8270149b87def7675b2a (patch)
treef4b8a14fc619b2e6ea297385e289b2549f6f21a4 /apt/progress/__init__.py
parentc62bdd0b1b633b134ba551847da0c1e8d12115c2 (diff)
parent930f6a2899b0b410777397fb206a8eba8c99100c (diff)
downloadpython-apt-d1b2c0fbd34cee0ffced8270149b87def7675b2a.tar.gz
merged from the debian-sid branch
Diffstat (limited to 'apt/progress/__init__.py')
-rw-r--r--apt/progress/__init__.py420
1 files changed, 25 insertions, 395 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)