summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Vogt <michael.vogt@ubuntu.com>2009-05-05 11:59:53 +0200
committerMichael Vogt <michael.vogt@ubuntu.com>2009-05-05 11:59:53 +0200
commit1b5b026c1013dc20b572a7fd2a690fe451248ab0 (patch)
tree521023c930ce6d98962beb7c6737e90a28dc9261
parent51c60cb3447247188bb8bf5c82b53e5199878b36 (diff)
downloadpython-apt-1b5b026c1013dc20b572a7fd2a690fe451248ab0.tar.gz
* apt/progress/__init__.py:
- add update_status_full() that takes file_size/partial_size as additional callback arguments - add pulse_items() that takes a addtional "items" tuple that gives the user full access to the individual items that are fetched * python/progress.cc: - low level code for update_status_full and pulse_items()
-rw-r--r--apt/progress/__init__.py20
-rw-r--r--debian/changelog14
-rw-r--r--doc/examples/progress.py18
-rw-r--r--po/python-apt.pot8
-rw-r--r--python/progress.cc126
5 files changed, 174 insertions, 12 deletions
diff --git a/apt/progress/__init__.py b/apt/progress/__init__.py
index 769942ce..3be197f7 100644
--- a/apt/progress/__init__.py
+++ b/apt/progress/__init__.py
@@ -112,6 +112,13 @@ class FetchProgress(object):
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.
@@ -125,6 +132,19 @@ class FetchProgress(object):
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."""
diff --git a/debian/changelog b/debian/changelog
index 83b00150..a3faab79 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,17 @@
+python-apt (0.7.10.4) UNRELEASED; urgency=low
+
+ [ Stephan Peijnik ]
+ * apt/progress/__init__.py:
+ - add update_status_full() that takes file_size/partial_size as
+ additional callback arguments
+ - add pulse_items() that takes a addtional "items" tuple that
+ gives the user full access to the individual items that are
+ fetched
+ * python/progress.cc:
+ - low level code for update_status_full and pulse_items()
+
+ -- Michael Vogt <michael.vogt@ubuntu.com> Tue, 05 May 2009 11:57:57 +0200
+
python-apt (0.7.10.3) unstable; urgency=low
* apt/package.py: Handle cases where no candidate is available, by returning
diff --git a/doc/examples/progress.py b/doc/examples/progress.py
index 2231001f..c007681f 100644
--- a/doc/examples/progress.py
+++ b/doc/examples/progress.py
@@ -35,15 +35,31 @@ class TextFetchProgress(apt.FetchProgress):
pass
def updateStatus(self, uri, descr, shortDescr, status):
- print "UpdateStatus: '%s' '%s' '%s' '%i'" % (
+ print "UpdateStatus: '%s' '%s' '%s' '%i' " % (
uri, descr, shortDescr, status)
+ def update_status_full(self, uri, descr, shortDescr, status, fileSize,
+ partialSize):
+ print "update_status_full: '%s' '%s' '%s' '%i' '%d/%d'" % (
+ uri, descr, shortDescr, status, partialSize, fileSize)
+
def pulse(self):
print "Pulse: CPS: %s/s; Bytes: %s/%s; Item: %s/%s" % (
apt.SizeToStr(self.currentCPS), apt.SizeToStr(self.currentBytes),
apt.SizeToStr(self.totalBytes), self.currentItems, self.totalItems)
return True
+ def pulse_items(self, items):
+ print "Pulse: CPS: %s/s; Bytes: %s/%s; Item: %s/%s" % (
+ apt.SizeToStr(self.currentCPS), apt.SizeToStr(self.currentBytes),
+ apt.SizeToStr(self.totalBytes), self.currentItems, self.totalItems)
+ print "Pulse-Items: "
+ for itm in items:
+ uri, descr, shortDescr, fileSize, partialSize = itm
+ print " - '%s' '%s' '%s' '%d/%d'" % (
+ uri, descr, shortDescr, partialSize, fileSize)
+ return True
+
def mediaChange(self, medium, drive):
print "Please insert medium %s in drive %s" % (medium, drive)
sys.stdin.readline()
diff --git a/po/python-apt.pot b/po/python-apt.pot
index 9c23c579..2a004600 100644
--- a/po/python-apt.pot
+++ b/po/python-apt.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-04-12 19:07+0200\n"
+"POT-Creation-Date: 2009-05-05 11:44+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -359,11 +359,11 @@ msgstr ""
msgid "Invalid unicode in description for '%s' (%s). Please report."
msgstr ""
-#: ../apt/package.py:745 ../apt/package.py:849
+#: ../apt/package.py:747 ../apt/package.py:851
msgid "The list of changes is not available"
msgstr ""
-#: ../apt/package.py:853
+#: ../apt/package.py:855
#, python-format
msgid ""
"The list of changes is not available yet.\n"
@@ -372,7 +372,7 @@ msgid ""
"until the changes become available or try again later."
msgstr ""
-#: ../apt/package.py:859
+#: ../apt/package.py:861
msgid ""
"Failed to download the list of changes. \n"
"Please check your Internet connection."
diff --git a/python/progress.cc b/python/progress.cc
index c5a1c138..14948d3c 100644
--- a/python/progress.cc
+++ b/python/progress.cc
@@ -9,10 +9,12 @@
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
+#include <map>
+#include <utility>
#include <apt-pkg/acquire-item.h>
+#include <apt-pkg/acquire-worker.h>
#include "progress.h"
-
// generic
bool PyCallbackObj::RunSimpleCallback(const char* method_name,
PyObject *arglist,
@@ -31,14 +33,16 @@ bool PyCallbackObj::RunSimpleCallback(const char* method_name,
return false;
}
PyObject *result = PyEval_CallObject(method, arglist);
+
Py_XDECREF(arglist);
if(result == NULL) {
// exception happend
std::cerr << "Error in function " << method_name << std::endl;
PyErr_Print();
+ PyErr_Clear();
- return NULL;
+ return false;
}
if(res != NULL)
*res = result;
@@ -84,6 +88,7 @@ void PyOpProgress::Done()
// apt interface
+
bool PyFetchProgress::MediaChange(string Media, string Drive)
{
//std::cout << "MediaChange" << std::endl;
@@ -104,8 +109,26 @@ bool PyFetchProgress::MediaChange(string Media, string Drive)
void PyFetchProgress::UpdateStatus(pkgAcquire::ItemDesc &Itm, int status)
{
//std::cout << "UpdateStatus: " << Itm.URI << " " << status << std::endl;
- PyObject *arglist = Py_BuildValue("(sssi)", Itm.URI.c_str(), Itm.Description.c_str(), Itm.ShortDesc.c_str(), status);
+
+ // Added object file size and object partial size to
+ // parameters that are passed to updateStatus.
+ // -- Stephan
+ PyObject *arglist = Py_BuildValue("(sssikk)", Itm.URI.c_str(),
+ Itm.Description.c_str(),
+ Itm.ShortDesc.c_str(),
+ status,
+ Itm.Owner->FileSize,
+ Itm.Owner->PartialSize);
+
+ RunSimpleCallback("update_status_full", arglist);
+
+ // legacy version of the interface
+ arglist = Py_BuildValue("(sssi)", Itm.URI.c_str(),
+ Itm.Description.c_str(),
+ Itm.ShortDesc.c_str(),
+ status);
RunSimpleCallback("updateStatus", arglist);
+
}
void PyFetchProgress::IMSHit(pkgAcquire::ItemDesc &Itm)
@@ -141,6 +164,28 @@ void PyFetchProgress::Start()
{
//std::cout << "Start" << std::endl;
pkgAcquireStatus::Start();
+
+ // These attributes should be initialized before the first callback (start)
+ // is invoked.
+ // -- Stephan
+ PyObject *o;
+
+ o = Py_BuildValue("f", 0.0f);
+ PyObject_SetAttrString(callbackInst, "currentCPS", o);
+ Py_XDECREF(o);
+ o = Py_BuildValue("f", 0.0f);
+ PyObject_SetAttrString(callbackInst, "currentBytes", o);
+ Py_XDECREF(o);
+ o = Py_BuildValue("i", 0);
+ PyObject_SetAttrString(callbackInst, "currentItems", o);
+ Py_XDECREF(o);
+ o = Py_BuildValue("i", 0);
+ PyObject_SetAttrString(callbackInst, "totalItems", o);
+ Py_XDECREF(o);
+ o = Py_BuildValue("f", 0.0f);
+ PyObject_SetAttrString(callbackInst, "totalBytes", o);
+ Py_XDECREF(o);
+
RunSimpleCallback("start");
}
@@ -178,12 +223,79 @@ bool PyFetchProgress::Pulse(pkgAcquire * Owner)
PyObject_SetAttrString(callbackInst, "totalBytes", o);
Py_XDECREF(o);
- PyObject *arglist = Py_BuildValue("()");
- PyObject *result;
- RunSimpleCallback("pulse", arglist, &result);
+ // Go through the list of items and add active items to the
+ // activeItems vector.
+ map<pkgAcquire::Worker *, pkgAcquire::ItemDesc *> activeItemMap;
+
+ for(pkgAcquire::Worker *Worker = Owner->WorkersBegin();
+ Worker != 0; Worker = Owner->WorkerStep(Worker)) {
+
+ if (Worker->CurrentItem == 0) {
+ // Ignore workers with no item running
+ continue;
+ }
+ activeItemMap.insert(std::make_pair(Worker, Worker->CurrentItem));
+ }
+
+ // Create the tuple that is passed as argument to pulse().
+ // This tuple contains activeItemMap.size() item tuples.
+ PyObject *arglist;
+
+ if (((int)activeItemMap.size()) > 0) {
+ PyObject *itemsTuple = PyTuple_New((Py_ssize_t) activeItemMap.size());
+
+ // Go through activeItems, create an item tuple in the form
+ // (URI, Description, ShortDesc, FileSize, PartialSize) and
+ // add that tuple to itemsTuple.
+ map<pkgAcquire::Worker *, pkgAcquire::ItemDesc *>::iterator iter;
+ int tuplePos;
+
+ for(tuplePos = 0, iter = activeItemMap.begin();
+ iter != activeItemMap.end(); ++iter, tuplePos++) {
+ pkgAcquire::Worker *worker = iter->first;
+ pkgAcquire::ItemDesc *itm = iter->second;
+
+ PyObject *itmTuple = Py_BuildValue("(ssskk)", itm->URI.c_str(),
+ itm->Description.c_str(),
+ itm->ShortDesc.c_str(),
+ worker->TotalSize,
+ worker->CurrentSize);
+ PyTuple_SetItem(itemsTuple, tuplePos, itmTuple);
+ }
+
+ // Now our itemsTuple is ready for being passed to pulse().
+ // pulse() is going to receive a single argument, being the
+ // tuple of items, which again contains one tuple with item
+ // information per item.
+ //
+ // Python Example:
+ //
+ // class MyFetchProgress(FetchProgress):
+ // def pulse(self, items):
+ // for itm in items:
+ // uri, desc, shortdesc, filesize, partialsize = itm
+ //
+ arglist = PyTuple_Pack(1, itemsTuple);
+ }
+ else {
+ arglist = Py_BuildValue("(())");
+ }
+ PyObject *result;
bool res = true;
- if(!PyArg_Parse(result, "b", &res))
+
+ RunSimpleCallback("pulse_items", arglist, &result);
+ if (result != NULL && PyArg_Parse(result, "b", &res) && res == false) {
+ // the user returned a explicit false here, stop
+ return false;
+ }
+
+ arglist = Py_BuildValue("()");
+ if (!RunSimpleCallback("pulse", arglist, &result)) {
+ return true;
+ }
+
+ if((result == NULL) || (!PyArg_Parse(result, "b", &res)))
{
// most of the time the user who subclasses the pulse()
// method forgot to add a return {True,False} so we just