clamp progress to 0..end, add possibility to receive whole lines only in processOutpu...
[enigma2.git] / lib / python / Components / Task.py
index fe81005b5dae1b5565701aa2c45ab2e5694d36b7..c59086e132cfbc4503044f956e7d78fbff1a1fc4 100644 (file)
@@ -3,14 +3,18 @@
 
 from Tools.CList import CList
 
-class Job:
+class Job(object):
        NOT_STARTED, IN_PROGRESS, FINISHED, FAILED = range(4)
        def __init__(self, name):
                self.tasks = [ ]
+               self.workspace = "/tmp"
                self.current_task = 0
                self.callback = None
                self.name = name
                self.finished = False
+               self.end = 100
+               self.__progress = 0
+               self.weightScale = 1
 
                self.state_changed = CList()
 
@@ -23,7 +27,20 @@ class Job:
        def createDescription(self):
                return None
 
+       def getProgress(self):
+               if self.current_task == len(self.tasks):
+                       return self.end
+               t = self.tasks[self.current_task]
+               jobprogress = t.weighting * t.progress / float(t.end) + sum([task.weighting for task in self.tasks[:self.current_task]])
+               return int(jobprogress*self.weightScale)
+
+       progress = property(getProgress)
+
+       def task_progress_changed_CB(self):
+               self.state_changed()
+
        def addTask(self, task):
+               task.job = self
                self.tasks.append(task)
 
        def start(self, callback):
@@ -32,6 +49,8 @@ class Job:
                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)
 
        def runNext(self):
                if self.current_task == len(self.tasks):
@@ -39,7 +58,7 @@ class Job:
                        self.status = self.FINISHED
                        self.state_changed()
                else:
-                       self.tasks[self.current_task].run(self.taskCallback)
+                       self.tasks[self.current_task].run(self.taskCallback,self.task_progress_changed_CB)
                        self.state_changed()
 
        def taskCallback(self, res):
@@ -49,21 +68,37 @@ class Job:
                        self.state_changed()
                        self.callback(self, res)
                else:
+                       self.state_changed();
                        self.current_task += 1
                        self.runNext()
 
-class Task:
-       def __init__(self, name):
+       def abort(self):
+               if self.current_task < len(self.tasks):
+                       self.tasks[self.current_task].abort()
+
+       def cancel(self):
+               # some Jobs might have a better idea of how to cancel a job
+               self.abort()
+
+class Task(object)     :
+       def __init__(self, job, name):
                self.name = name
-               self.workspace = "/tmp"
                self.immediate_preconditions = [ ]
                self.global_preconditions = [ ]
                self.postconditions = [ ]
                self.returncode = None
                self.initial_input = None
+               self.job = None
 
+               self.end = 100
+               self.weighting = 100
+               self.__progress = 0
                self.cmd = None
+               self.cwd = "/tmp"
                self.args = [ ]
+               self.task_progress_changed = None
+               self.output_line = ""
+               job.addTask(self)
 
        def setCommandline(self, cmd, args):
                self.cmd = cmd
@@ -86,13 +121,15 @@ class Task:
                                not_met.append(precondition)
                return not_met
 
-       def run(self, callback):
-               failed_preconditions = self.checkPreconditions(True)
+       def run(self, callback, task_progress_changed):
+               failed_preconditions = self.checkPreconditions(True) + self.checkPreconditions(False)
                if len(failed_preconditions):
-                       errback(failed_preconditions)
+                       callback(failed_preconditions)
                        return
+               self.prepare()
 
                self.callback = callback
+               self.task_progress_changed = task_progress_changed
                from enigma import eConsoleAppContainer
                self.container = eConsoleAppContainer()
                self.container.appClosed.get().append(self.processFinished)
@@ -101,6 +138,9 @@ class Task:
                assert self.cmd is not None
                assert len(self.args) >= 1
 
+               if self.cwd is not None:
+                       self.container.setCWD(self.cwd)
+
                print "execute:", self.container.execute(self.cmd, self.args), self.cmd, self.args
                if self.initial_input:
                        self.writeInput(self.initial_input)
@@ -112,19 +152,36 @@ class Task:
                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):
                self.returncode = returncode
                self.finish()
 
-       def finish(self):
+       def abort(self):
+               self.container.kill()
+               self.finish(aborted = True)
+
+       def finish(self, aborted = False):
                self.afterRun()
                not_met = [ ]
-               for postcondition in self.postconditions:
-                       if not postcondition.check(self):
-                               not_met.append(postcondition)
+               if aborted:
+                       not_met.append(AbortedPostcondition())
+               else:
+                       for postcondition in self.postconditions:
+                               if not postcondition.check(self):
+                                       not_met.append(postcondition)
 
+               self.cleanup(not_met)
                self.callback(not_met)
 
        def afterRun(self):
@@ -133,6 +190,20 @@ class Task:
        def writeInput(self, input):
                self.container.write(input)
 
+       def getProgress(self):
+               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)
+
 class JobManager:
        def __init__(self):
                self.active_jobs = [ ]
@@ -192,18 +263,34 @@ 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 WorkspaceExistsPrecondition:
+       def check(self, task):
+               return os.access(task.job.workspace, os.W_OK)
+
+class DiskspacePrecondition:
+       def __init__(self, diskspace_required):
+               self.diskspace_required = diskspace_required
+
+       def check(self, task):
+               import os
+               try:
+                       s = os.statvfs(task.job.workspace)
+                       return s.f_bsize * s.f_bavail >= self.diskspace_required
+               except OSError:
+                       return False
+
 class ToolExistsPrecondition:
        def check(self, task):
                import os
-               return os.access(task.cmd, os.X_OK)
+               if task.cmd[0]=='/':
+                       realpath = task.cmd
+               else:
+                       realpath = self.cwd + '/' + self.cmd
+               return os.access(realpath, os.X_OK)
+
+class AbortedPostcondition:
+       pass
 
 class ReturncodePostcondition:
        def check(self, task):