X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/8b5176ab8d8a334dfee75cfbda878240e2501823..98f7390aeb2c7b1ef3963fef83a6eeae367256a6:/lib/python/Components/Task.py diff --git a/lib/python/Components/Task.py b/lib/python/Components/Task.py index aebca732..07b9d3f5 100644 --- a/lib/python/Components/Task.py +++ b/lib/python/Components/Task.py @@ -46,32 +46,41 @@ class Job(object): def start(self, callback): assert self.callback is None self.callback = callback + self.restart() + + def restart(self): self.status = self.IN_PROGRESS self.state_changed() self.runNext() sumTaskWeightings = sum([t.weighting for t in self.tasks]) - self.weightScale = (self.end+1) / float(sumTaskWeightings) + self.weightScale = self.end / float(sumTaskWeightings) def runNext(self): if self.current_task == len(self.tasks): - self.callback(self, []) + cb = self.callback + self.callback = None self.status = self.FINISHED self.state_changed() + cb(self, None, []) else: - self.tasks[self.current_task].run(self.taskCallback,self.task_progress_changed_CB) + self.tasks[self.current_task].run(self.taskCallback, self.task_progress_changed_CB) self.state_changed() - def taskCallback(self, res): + def taskCallback(self, task, res): if len(res): print ">>> Error:", res self.status = self.FAILED self.state_changed() - self.callback(self, res) + self.callback(self, task, res) else: self.state_changed(); self.current_task += 1 self.runNext() + def retry(self): + assert self.status == self.FAILED + self.restart() + def abort(self): if self.current_task < len(self.tasks): self.tasks[self.current_task].abort() @@ -80,7 +89,7 @@ class Job(object): # some Jobs might have a better idea of how to cancel a job self.abort() -class Task(object) : +class Task(object): def __init__(self, job, name): self.name = name self.immediate_preconditions = [ ] @@ -97,6 +106,7 @@ class Task(object) : self.cwd = "/tmp" self.args = [ ] self.task_progress_changed = None + self.output_line = "" job.addTask(self) def setCommandline(self, cmd, args): @@ -123,7 +133,7 @@ class Task(object) : def run(self, callback, task_progress_changed): failed_preconditions = self.checkPreconditions(True) + self.checkPreconditions(False) if len(failed_preconditions): - callback(failed_preconditions) + callback(self, failed_preconditions) return self.prepare() @@ -151,6 +161,15 @@ class Task(object) : pass def processOutput(self, data): + self.output_line += data + while True: + i = self.output_line.find('\n') + if i == -1: + break + self.processOutputLine(self.output_line[:i+1]) + self.output_line = self.output_line[i+1:] + + def processOutputLine(self, line): pass def processFinished(self, returncode): @@ -171,7 +190,8 @@ class Task(object) : if not postcondition.check(self): not_met.append(postcondition) - self.callback(not_met) + self.cleanup(not_met) + self.callback(self, not_met) def afterRun(self): pass @@ -183,12 +203,19 @@ class Task(object) : return self.__progress def setProgress(self, progress): + if progress > self.end: + progress = self.end + if progress < 0: + progress = 0 print "progress now", progress self.__progress = progress self.task_progress_changed() progress = property(getProgress, setProgress) +# The jobmanager will execute multiple jobs, each after another. +# later, it will also support suspending jobs (and continuing them after reboot etc) +# It also supports a notification when some error occured, and possibly a retry. class JobManager: def __init__(self): self.active_jobs = [ ] @@ -206,14 +233,28 @@ class JobManager: self.active_job = self.active_jobs.pop(0) self.active_job.start(self.jobDone) - def jobDone(self, job, problems): - print "job", job, "completed with", problems + def jobDone(self, job, task, problems): + print "job", job, "completed with", problems, "in", task if problems: - self.failed_jobs.append(self.active_job) + from Tools import Notifications + from Screens.MessageBox import MessageBox + Notifications.AddNotificationWithCallback(self.errorCB, MessageBox, _("Error: %s\nRetry?") % (problems[0].getErrorMessage(task))) + return + #self.failed_jobs.append(self.active_job) self.active_job = None self.kick() + def errorCB(self, answer): + if answer: + print "retrying job" + self.active_job.retry() + else: + print "not retrying job." + self.failed_jobs.append(self.active_job) + self.active_job = None + self.kick() + # some examples: #class PartitionExistsPostcondition: # def __init__(self, device): @@ -248,28 +289,51 @@ class JobManager: # if filesystem is not None: # self.args += ["-t", filesystem] # self.args.append(device + "part%d" % partition) -# -#class DiskspacePrecondition: -# def __init__(self, diskspace_required): -# self.diskspace_required = diskspace_required -# -# def check(self, task): -# return getFreeDiskspace(task.workspace) >= self.diskspace_required -# -class ToolExistsPrecondition: +class Condition: + RECOVERABLE = False + + def getErrorMessage(self, task): + return _("An error has occured. (%s)") % (self.__class__.__name__) + +class WorkspaceExistsPrecondition(Condition): + def check(self, task): + return os.access(task.job.workspace, os.W_OK) + +class DiskspacePrecondition(Condition): + def __init__(self, diskspace_required): + self.diskspace_required = diskspace_required + self.diskspace_available = None + + def check(self, task): + import os + try: + s = os.statvfs(task.job.workspace) + self.diskspace_available = s.f_bsize * s.f_bavail + return self.diskspace_available >= self.diskspace_required + except OSError: + return False + + def getErrorMessage(self, task): + return _("Not enough diskspace. Please free up some diskspace and try again. (%d MB required, %d MB available)") % (self.diskspace_required / 1024 / 1024, self.diskspace_available / 1024 / 1024) + +class ToolExistsPrecondition(Condition): def check(self, task): import os if task.cmd[0]=='/': realpath = task.cmd else: - realpath = self.cwd + '/' + self.cmd + realpath = task.cwd + '/' + task.cmd + self.realpath = realpath return os.access(realpath, os.X_OK) -class AbortedPostcondition: + def getErrorMessage(self, task): + return _("A required tool (%s) was not found.") % (self.realpath) + +class AbortedPostcondition(Condition): pass -class ReturncodePostcondition: +class ReturncodePostcondition(Condition): def check(self, task): return task.returncode == 0