diff options
| -rw-r--r-- | apt/progress/__init__.py | 20 | ||||
| -rw-r--r-- | debian/changelog | 14 | ||||
| -rw-r--r-- | doc/examples/progress.py | 18 | ||||
| -rw-r--r-- | po/python-apt.pot | 20 | ||||
| -rw-r--r-- | python/progress.cc | 124 |
5 files changed, 179 insertions, 17 deletions
diff --git a/apt/progress/__init__.py b/apt/progress/__init__.py index 6e4c6eec..47d2a861 100644 --- a/apt/progress/__init__.py +++ b/apt/progress/__init__.py @@ -116,6 +116,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. @@ -129,6 +136,19 @@ class FetchProgress(object): float(self.current_cps)) 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.current_bytes + self.current_items) * 100.0) / + float(self.total_bytes + self.total_items)) + if self.current_cps > 0: + self.eta = ((self.total_bytes - self.current_bytes) / + float(self.current_cps)) + return True + def media_change(self, medium, drive): """react to media change events.""" diff --git a/debian/changelog b/debian/changelog index 55e68a52..ca215554 100644 --- a/debian/changelog +++ b/debian/changelog @@ -36,6 +36,20 @@ python-apt (0.7.90) experimental; urgency=low -- Julian Andres Klode <jak@debian.org> Wed, 15 Apr 2009 13:47:42 +0200 +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 3fcd395a..5920f93a 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-16 19:54+0200\n" +"POT-Creation-Date: 2009-06-08 16:52+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" @@ -331,39 +331,39 @@ msgstr "" msgid "Custom servers" msgstr "" -#: ../apt/progress/gtk2.py:246 +#: ../apt/progress/gtk2.py:258 #, python-format msgid "Downloading file %(current)li of %(total)li with %(speed)s/s" msgstr "" -#: ../apt/progress/gtk2.py:252 +#: ../apt/progress/gtk2.py:264 #, python-format msgid "Downloading file %(current)li of %(total)li" msgstr "" #. Setup some child widgets -#: ../apt/progress/gtk2.py:272 +#: ../apt/progress/gtk2.py:284 msgid "Details" msgstr "" -#: ../apt/progress/gtk2.py:354 +#: ../apt/progress/gtk2.py:366 msgid "Starting..." msgstr "" -#: ../apt/progress/gtk2.py:360 +#: ../apt/progress/gtk2.py:372 msgid "Complete" msgstr "" -#: ../apt/package.py:286 +#: ../apt/package.py:301 #, python-format msgid "Invalid unicode in description for '%s' (%s). Please report." msgstr "" -#: ../apt/package.py:846 ../apt/package.py:950 +#: ../apt/package.py:861 ../apt/package.py:965 msgid "The list of changes is not available" msgstr "" -#: ../apt/package.py:954 +#: ../apt/package.py:969 #, 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:960 +#: ../apt/package.py:975 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 8214a789..39124df1 100644 --- a/python/progress.cc +++ b/python/progress.cc @@ -9,7 +9,10 @@ #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 @@ -30,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; @@ -89,6 +94,7 @@ void PyOpProgress::Done() // apt interface + bool PyFetchProgress::MediaChange(string Media, string Drive) { //std::cout << "MediaChange" << std::endl; @@ -112,7 +118,24 @@ 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); if(PyObject_HasAttrString(callbackInst, "update_status")) RunSimpleCallback("update_status", arglist); else @@ -152,6 +175,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"); } @@ -204,12 +249,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 |
