diff options
Diffstat (limited to 'python/progress.cc')
-rw-r--r-- | python/progress.cc | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/python/progress.cc b/python/progress.cc new file mode 100644 index 0000000..f4ed8e6 --- /dev/null +++ b/python/progress.cc @@ -0,0 +1,473 @@ +// Description /*{{{*/ +// $Id: progress.cc,v 1.5 2003/06/03 03:03:23 mvo Exp $ +/* ###################################################################### + + Progress - Wrapper for the progress related functions + + ##################################################################### */ +#include <Python.h> +#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 <apt-pkg/install-progress.h> +#include "progress.h" +#include "generic.h" +#include "apt_pkgmodule.h" + +/** + * Set an attribute on an object, after creating the value with + * Py_BuildValue(fmt, arg). Afterwards, decrease its refcount and return + * whether setting the attribute was successful. + */ +template<class T> +inline bool setattr(PyObject *object, const char *attr, const char *fmt, T arg) +{ + if (!object) + return false; + PyObject *value = Py_BuildValue(fmt, arg); + if (value == NULL) + return false; + + int result = PyObject_SetAttrString(object, attr, value); + Py_DECREF(value); + return result != -1; +} + +inline PyObject *TUPLEIZE(PyObject *op) { + PyObject *ret = Py_BuildValue("(O)", op); + Py_DECREF(op); + return ret; +} + +// generic +bool PyCallbackObj::RunSimpleCallback(const char* method_name, + PyObject *arglist, + PyObject **res) +{ + if(callbackInst == 0) { + Py_XDECREF(arglist); + return false; + } + + PyObject *method = PyObject_GetAttrString(callbackInst,(char*) method_name); + if(method == NULL) { + Py_XDECREF(arglist); + if (res) { + Py_INCREF(Py_None); + *res = Py_None; + } + return false; + } + + PyObject *result = PyObject_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 false; + } + if(res != NULL) + *res = result; + else + Py_XDECREF(result); + Py_XDECREF(method); + + return true; +} + + +// OpProgress interface +void PyOpProgress::Update() +{ + // Build up the argument list... + if(!CheckChange(0.7)) + return; + + setattr(callbackInst, "op", "s", Op.c_str()); + setattr(callbackInst, "subop", "s", SubOp.c_str()); + setattr(callbackInst, "major_change", "b", MajorChange); + setattr(callbackInst, "percent", "N", MkPyNumber(Percent)); + RunSimpleCallback("update"); +} + +void PyOpProgress::Done() +{ + RunSimpleCallback("done"); +} + + + +// fetcher interface + + + +// apt interface + +PyObject *PyFetchProgress::GetDesc(pkgAcquire::ItemDesc *item) { + if (!pyAcquire && item->Owner && item->Owner->GetOwner()) { + pyAcquire = PyAcquire_FromCpp(item->Owner->GetOwner(), false, NULL); + } + PyObject *pyItem = PyAcquireItem_FromCpp(item->Owner, false, pyAcquire); + PyObject *pyDesc = PyAcquireItemDesc_FromCpp(item, false, pyItem); + Py_DECREF(pyItem); + return pyDesc; +} + +bool PyFetchProgress::MediaChange(std::string Media, std::string Drive) +{ + PyCbObj_END_ALLOW_THREADS + PyObject *arglist = Py_BuildValue("(ss)", Media.c_str(), Drive.c_str()); + PyObject *result = NULL; + + if(PyObject_HasAttrString(callbackInst, "mediaChange")) + RunSimpleCallback("mediaChange", arglist, &result); + else + RunSimpleCallback("media_change", arglist, &result); + + bool res = true; + if(!PyArg_Parse(result, "b", &res)) { + // no return value or None, assume false + PyCbObj_BEGIN_ALLOW_THREADS + return false; + } + + PyCbObj_BEGIN_ALLOW_THREADS + return res; +} + +void PyFetchProgress::UpdateStatus(pkgAcquire::ItemDesc &Itm, int status) +{ + // Added object file size and object partial size to + // parameters that are passed to updateStatus. + // -- Stephan + PyObject *arglist = Py_BuildValue("(sssNNN)", Itm.URI.c_str(), + Itm.Description.c_str(), + Itm.ShortDesc.c_str(), + MkPyNumber(status), + MkPyNumber(Itm.Owner->FileSize), + MkPyNumber(Itm.Owner->PartialSize)); + + RunSimpleCallback("update_status_full", arglist); + + // legacy version of the interface + + arglist = Py_BuildValue("(sssN)", Itm.URI.c_str(), Itm.Description.c_str(), + Itm.ShortDesc.c_str(), MkPyNumber(status)); + + if(PyObject_HasAttrString(callbackInst, "updateStatus")) + RunSimpleCallback("updateStatus", arglist); + else + RunSimpleCallback("update_status", arglist); +} + +void PyFetchProgress::IMSHit(pkgAcquire::ItemDesc &Itm) +{ + PyCbObj_END_ALLOW_THREADS + if (PyObject_HasAttrString(callbackInst, "ims_hit")) + RunSimpleCallback("ims_hit", TUPLEIZE(GetDesc(&Itm))); + else + UpdateStatus(Itm, DLHit); + PyCbObj_BEGIN_ALLOW_THREADS +} + +void PyFetchProgress::Fetch(pkgAcquire::ItemDesc &Itm) +{ + PyCbObj_END_ALLOW_THREADS + if (PyObject_HasAttrString(callbackInst, "fetch")) + RunSimpleCallback("fetch", TUPLEIZE(GetDesc(&Itm))); + else + UpdateStatus(Itm, DLQueued); + PyCbObj_BEGIN_ALLOW_THREADS +} + +void PyFetchProgress::Done(pkgAcquire::ItemDesc &Itm) +{ + PyCbObj_END_ALLOW_THREADS + if (PyObject_HasAttrString(callbackInst, "done")) + RunSimpleCallback("done", TUPLEIZE(GetDesc(&Itm))); + else + UpdateStatus(Itm, DLDone); + PyCbObj_BEGIN_ALLOW_THREADS +} + +void PyFetchProgress::Fail(pkgAcquire::ItemDesc &Itm) +{ + PyCbObj_END_ALLOW_THREADS + if (PyObject_HasAttrString(callbackInst, "fail")) { + RunSimpleCallback("fail", TUPLEIZE(GetDesc(&Itm))); + PyCbObj_BEGIN_ALLOW_THREADS + return; + } + + // Ignore certain kinds of transient failures (bad code) + if (Itm.Owner->Status == pkgAcquire::Item::StatIdle) { + PyCbObj_BEGIN_ALLOW_THREADS + return; + } + + if (Itm.Owner->Status == pkgAcquire::Item::StatDone) + { + UpdateStatus(Itm, DLIgnored); + } + + + if (PyObject_HasAttrString(callbackInst, "fail")) + RunSimpleCallback("fail", TUPLEIZE(GetDesc(&Itm))); + else + UpdateStatus(Itm, DLFailed); + PyCbObj_BEGIN_ALLOW_THREADS +} + +void PyFetchProgress::Start() +{ + pkgAcquireStatus::Start(); + + + RunSimpleCallback("start"); + /* After calling the start method we can safely allow + * other Python threads to do their work for now. + */ + PyCbObj_BEGIN_ALLOW_THREADS +} + + +void PyFetchProgress::Stop() +{ + /* After the stop operation occurred no other threads + * are allowed. This is done so we have a matching + * PyCbObj_END_ALLOW_THREADS to our previous + * PyCbObj_BEGIN_ALLOW_THREADS (Python requires this!). + */ + + PyCbObj_END_ALLOW_THREADS + pkgAcquireStatus::Stop(); + RunSimpleCallback("stop"); +} + +bool PyFetchProgress::Pulse(pkgAcquire * Owner) +{ + PyCbObj_END_ALLOW_THREADS + pkgAcquireStatus::Pulse(Owner); + + if(callbackInst == 0) { + PyCbObj_BEGIN_ALLOW_THREADS + return false; + } + + setattr(callbackInst, "last_bytes", "N", MkPyNumber(LastBytes)); + setattr(callbackInst, "current_cps", "N", MkPyNumber(CurrentCPS)); + setattr(callbackInst, "current_bytes", "N", MkPyNumber(CurrentBytes)); + setattr(callbackInst, "total_bytes", "N", MkPyNumber(TotalBytes)); + setattr(callbackInst, "fetched_bytes", "N", MkPyNumber(FetchedBytes)); + setattr(callbackInst, "elapsed_time", "N", MkPyNumber(ElapsedTime)); + setattr(callbackInst, "current_items", "N", MkPyNumber(CurrentItems)); + setattr(callbackInst, "total_items", "N", MkPyNumber(TotalItems)); + + // New style + if (!PyObject_HasAttrString(callbackInst, "updateStatus")) { + PyObject *result1; + bool res1 = true; + + if (pyAcquire == NULL) { + pyAcquire = PyAcquire_FromCpp(Owner, false, NULL); + } + Py_INCREF(pyAcquire); + + if (RunSimpleCallback("pulse", TUPLEIZE(pyAcquire) , &result1)) { + if (result1 != NULL && + result1 != Py_None && + PyArg_Parse(result1, "b", &res1) && + res1 == false) { + // the user returned a explicit false here, stop + PyCbObj_BEGIN_ALLOW_THREADS + return false; + } + } + PyCbObj_BEGIN_ALLOW_THREADS + return true; + } + return false; +} + + + +// install progress + +void PyInstallProgress::StartUpdate() +{ + RunSimpleCallback("start_update"); + PyCbObj_BEGIN_ALLOW_THREADS +} + +void PyInstallProgress::UpdateInterface() +{ + PyCbObj_END_ALLOW_THREADS + RunSimpleCallback("update_interface"); + PyCbObj_BEGIN_ALLOW_THREADS +} + +void PyInstallProgress::FinishUpdate() +{ + PyCbObj_END_ALLOW_THREADS + RunSimpleCallback("finish_update"); +} + +pkgPackageManager::OrderResult PyInstallProgress::Run(pkgPackageManager *pm) +{ + pkgPackageManager::OrderResult res; + int ret; + pid_t child_id; + +#if 0 // FIXME: this needs to be merged into apt to support medium swaping + res = pm->DoInstallPreFork(); + if (res == pkgPackageManager::Failed) + return res; +#endif + + // support custom fork methods + if(PyObject_HasAttrString(callbackInst, "fork")) { + PyObject *method = PyObject_GetAttrString(callbackInst, "fork"); + PyObject *arglist = Py_BuildValue("()"); + PyObject *result = PyObject_CallObject(method, arglist); + Py_DECREF(arglist); + if (result == NULL) { + std::cerr << "fork method invalid" << std::endl; + PyErr_Print(); + return pkgPackageManager::Failed; + } + if(!PyArg_Parse(result, "i", &child_id) ) { + std::cerr << "custom fork() result could not be parsed?"<< std::endl; + return pkgPackageManager::Failed; + } + } else { + child_id = fork(); + } + + PyObject *child_o = MkPyNumber(child_id); + PyObject_SetAttrString(callbackInst, "child_pid", child_o); + Py_DECREF(child_o); + +#if 0 // FIXME: this needs to be merged into apt to support medium swaping + if (child_id == 0) { + res = pm->DoInstallPostFork(); + _exit(res); + } +#endif + if (child_id == 0) { + PyObject *v = PyObject_GetAttrString(callbackInst, "writefd"); + if(v) { + int fd = PyObject_AsFileDescriptor(v); + + APT::Progress::PackageManagerProgressFd progress(fd); + res = pm->DoInstall(&progress); + } else { + APT::Progress::PackageManagerProgressFd progress(-1); + res = pm->DoInstall(&progress); + } + _exit(res); + } + + StartUpdate(); + + + PyCbObj_END_ALLOW_THREADS + if(PyObject_HasAttrString(callbackInst, "waitChild") || + PyObject_HasAttrString(callbackInst, "wait_child")) { + PyObject *method; + if (PyObject_HasAttrString(callbackInst, "waitChild")) + method = PyObject_GetAttrString(callbackInst, "waitChild"); + else + method = PyObject_GetAttrString(callbackInst, "wait_child"); + PyObject *result = PyObject_CallObject(method, NULL); + if (result == NULL) { + std::cerr << "waitChild method invalid" << std::endl; + PyErr_Print(); + PyCbObj_BEGIN_ALLOW_THREADS + return pkgPackageManager::Failed; + } + if(!PyArg_Parse(result, "i", &res) ) { + std::cerr << "custom waitChild() result could not be parsed?"<< std::endl; + PyCbObj_BEGIN_ALLOW_THREADS + return pkgPackageManager::Failed; + } + PyCbObj_BEGIN_ALLOW_THREADS + } else { + PyCbObj_BEGIN_ALLOW_THREADS + while (waitpid(child_id, &ret, WNOHANG) == 0) { + PyCbObj_END_ALLOW_THREADS + UpdateInterface(); + PyCbObj_BEGIN_ALLOW_THREADS + } + + res = (pkgPackageManager::OrderResult) WEXITSTATUS(ret); + } + + FinishUpdate(); + + return res; +} + + +//----------------------------------------------------------------------------- +// apt-cdrom interface + +void PyCdromProgress::Update(std::string text, int current) +{ + PyObject *arglist = Py_BuildValue("(si)", text.c_str(), current); + setattr(callbackInst, "total_steps", "i", totalSteps); + RunSimpleCallback("update", arglist); +} + +bool PyCdromProgress::ChangeCdrom() +{ + PyObject *arglist = Py_BuildValue("()"); + PyObject *result = NULL; + if (PyObject_HasAttrString(callbackInst, "changeCdrom")) + RunSimpleCallback("changeCdrom", arglist, &result); + else + RunSimpleCallback("change_cdrom", arglist, &result); + + bool res = true; + if(!PyArg_Parse(result, "b", &res)) + std::cerr << "ChangeCdrom: result could not be parsed" << std::endl; + + return res; +} + + +bool PyCdromProgress::AskCdromName(std::string &Name) +{ + PyObject *arglist = Py_BuildValue("()"); + const char *new_name; + bool res; + PyObject *result = NULL; + + // Old style: (True, name) on success, (False, name) on failure. + if (PyObject_HasAttrString(callbackInst, "askAdromName")) { + RunSimpleCallback("askAdromName", arglist, &result); + if(!PyArg_Parse(result, "(bs)", &res, &new_name)) + std::cerr << "AskCdromName: result could not be parsed" << std::endl; + // set the new name + Name =std:: string(new_name); + return res; + } + // New style: String on success, None on failure. + else { + RunSimpleCallback("ask_cdrom_name", arglist, &result); + if(result == Py_None) + return false; + if(!PyArg_Parse(result, "s", &new_name)) + std::cerr << "ask_cdrom_name: result could not be parsed" << std::endl; + else + Name = std::string(new_name); + return true; + } +} |