Delete Task.py
This commit is contained in:
@@ -1,523 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#from __future__ import print_function
|
||||
#from Plugins.Extensions.NeoBoot.__init__ import _
|
||||
from Tools.CList import CList
|
||||
|
||||
class Job(object):
|
||||
NOT_STARTED, IN_PROGRESS, FINISHED, FAILED = range(4)
|
||||
|
||||
def __init__(self, name):
|
||||
self.tasks = []
|
||||
self.resident_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.afterEvent = None
|
||||
self.state_changed = CList()
|
||||
self.status = self.NOT_STARTED
|
||||
self.onSuccess = None
|
||||
return
|
||||
|
||||
def fromDescription(self, description):
|
||||
pass
|
||||
|
||||
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 getStatustext(self):
|
||||
return {self.NOT_STARTED: _('Waiting'),
|
||||
self.IN_PROGRESS: _('In progress'),
|
||||
self.FINISHED: _('Finished'),
|
||||
self.FAILED: _('Failed')}[self.status]
|
||||
|
||||
def task_progress_changed_CB(self):
|
||||
self.state_changed()
|
||||
|
||||
def addTask(self, task):
|
||||
task.job = self
|
||||
task.task_progress_changed = self.task_progress_changed_CB
|
||||
self.tasks.append(task)
|
||||
|
||||
def start(self, callback):
|
||||
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 ]) or 1
|
||||
self.weightScale = self.end / float(sumTaskWeightings)
|
||||
|
||||
def runNext(self):
|
||||
if self.current_task == len(self.tasks):
|
||||
if len(self.resident_tasks) == 0:
|
||||
self.status = self.FINISHED
|
||||
self.state_changed()
|
||||
self.callback(self, None, [])
|
||||
self.callback = None
|
||||
else:
|
||||
print 'still waiting for %d resident task(s) %s to finish' % (len(self.resident_tasks), str(self.resident_tasks))
|
||||
else:
|
||||
self.tasks[self.current_task].run(self.taskCallback)
|
||||
self.state_changed()
|
||||
return
|
||||
|
||||
def taskCallback(self, task, res, stay_resident = False):
|
||||
cb_idx = self.tasks.index(task)
|
||||
if stay_resident:
|
||||
if cb_idx not in self.resident_tasks:
|
||||
self.resident_tasks.append(self.current_task)
|
||||
print 'task going resident:', task
|
||||
else:
|
||||
print 'task keeps staying resident:', task
|
||||
return
|
||||
if len(res):
|
||||
print '>>> Error:', res
|
||||
self.status = self.FAILED
|
||||
self.state_changed()
|
||||
self.callback(self, task, res)
|
||||
if cb_idx != self.current_task:
|
||||
if cb_idx in self.resident_tasks:
|
||||
print 'resident task finished:', task
|
||||
self.resident_tasks.remove(cb_idx)
|
||||
if res == []:
|
||||
self.state_changed()
|
||||
self.current_task += 1
|
||||
self.runNext()
|
||||
|
||||
def retry(self):
|
||||
self.restart()
|
||||
|
||||
def abort(self):
|
||||
if self.current_task < len(self.tasks):
|
||||
self.tasks[self.current_task].abort()
|
||||
for i in self.resident_tasks:
|
||||
self.tasks[i].abort()
|
||||
|
||||
def cancel(self):
|
||||
self.abort()
|
||||
|
||||
def __str__(self):
|
||||
return 'Components.Task.Job name=%s #tasks=%s' % (self.name, len(self.tasks))
|
||||
|
||||
|
||||
class Task(object):
|
||||
|
||||
def __init__(self, job, name):
|
||||
self.name = name
|
||||
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.cmdline = None
|
||||
self.task_progress_changed = None
|
||||
self.output_line = ''
|
||||
job.addTask(self)
|
||||
self.container = None
|
||||
return
|
||||
|
||||
def setCommandline(self, cmd, args):
|
||||
self.cmd = cmd
|
||||
self.args = args
|
||||
|
||||
def setTool(self, tool):
|
||||
self.cmd = tool
|
||||
self.args = [tool]
|
||||
self.global_preconditions.append(ToolExistsPrecondition())
|
||||
self.postconditions.append(ReturncodePostcondition())
|
||||
|
||||
def setCmdline(self, cmdline):
|
||||
self.cmdline = cmdline
|
||||
|
||||
def checkPreconditions(self, immediate = False):
|
||||
not_met = []
|
||||
if immediate:
|
||||
preconditions = self.immediate_preconditions
|
||||
else:
|
||||
preconditions = self.global_preconditions
|
||||
for precondition in preconditions:
|
||||
if not precondition.check(self):
|
||||
not_met.append(precondition)
|
||||
|
||||
return not_met
|
||||
|
||||
def _run(self):
|
||||
if self.cmd is None and self.cmdline is None:
|
||||
self.finish()
|
||||
return
|
||||
else:
|
||||
from enigma import eConsoleAppContainer
|
||||
self.container = eConsoleAppContainer()
|
||||
self.container.appClosed.append(self.processFinished)
|
||||
self.container.stdoutAvail.append(self.processStdout)
|
||||
self.container.stderrAvail.append(self.processStderr)
|
||||
if self.cwd is not None:
|
||||
self.container.setCWD(self.cwd)
|
||||
if not self.cmd and self.cmdline:
|
||||
print 'execute:', self.container.execute(self.cmdline), self.cmdline
|
||||
else:
|
||||
print 'execute:', self.container.execute(self.cmd, *self.args), ' '.join(self.args)
|
||||
if self.initial_input:
|
||||
self.writeInput(self.initial_input)
|
||||
return
|
||||
return
|
||||
|
||||
def run(self, callback):
|
||||
failed_preconditions = self.checkPreconditions(True) + self.checkPreconditions(False)
|
||||
if failed_preconditions:
|
||||
print '[Task] preconditions failed'
|
||||
callback(self, failed_preconditions)
|
||||
return
|
||||
self.callback = callback
|
||||
try:
|
||||
self.prepare()
|
||||
self._run()
|
||||
except Exception as ex:
|
||||
print '[Task] exception:', ex
|
||||
self.postconditions = [FailedPostcondition(ex)]
|
||||
self.finish()
|
||||
|
||||
def prepare(self):
|
||||
pass
|
||||
|
||||
def cleanup(self, failed):
|
||||
pass
|
||||
|
||||
def processStdout(self, data):
|
||||
self.processOutput(data)
|
||||
|
||||
def processStderr(self, data):
|
||||
self.processOutput(data)
|
||||
|
||||
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):
|
||||
print '[Task %s]' % self.name, line[:-1]
|
||||
|
||||
def processFinished(self, returncode):
|
||||
self.returncode = returncode
|
||||
self.finish()
|
||||
|
||||
def abort(self):
|
||||
if self.container:
|
||||
self.container.kill()
|
||||
self.finish(aborted=True)
|
||||
|
||||
def finish(self, aborted = False):
|
||||
self.afterRun()
|
||||
not_met = []
|
||||
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(self, not_met)
|
||||
|
||||
def afterRun(self):
|
||||
pass
|
||||
|
||||
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
|
||||
self.__progress = progress
|
||||
if self.task_progress_changed:
|
||||
self.task_progress_changed()
|
||||
|
||||
progress = property(getProgress, setProgress)
|
||||
|
||||
def __str__(self):
|
||||
return 'Components.Task.Task name=%s' % self.name
|
||||
|
||||
|
||||
class LoggingTask(Task):
|
||||
|
||||
def __init__(self, job, name):
|
||||
Task.__init__(self, job, name)
|
||||
self.log = []
|
||||
|
||||
def processOutput(self, data):
|
||||
print '[%s]' % self.name, data,
|
||||
self.log.append(data)
|
||||
|
||||
|
||||
class PythonTask(Task):
|
||||
|
||||
def _run(self):
|
||||
from twisted.internet import threads
|
||||
from enigma import eTimer
|
||||
self.aborted = False
|
||||
self.pos = 0
|
||||
threads.deferToThread(self.work).addBoth(self.onComplete)
|
||||
self.timer = eTimer()
|
||||
self.timer.callback.append(self.onTimer)
|
||||
self.timer.start(5)
|
||||
|
||||
def work(self):
|
||||
raise NotImplemented, 'work'
|
||||
|
||||
def abort(self):
|
||||
self.aborted = True
|
||||
if self.callback is None:
|
||||
self.finish(aborted=True)
|
||||
return
|
||||
|
||||
def onTimer(self):
|
||||
self.setProgress(self.pos)
|
||||
|
||||
def onComplete(self, result):
|
||||
self.postconditions.append(FailedPostcondition(result))
|
||||
self.timer.stop()
|
||||
del self.timer
|
||||
self.finish()
|
||||
|
||||
|
||||
class ConditionTask(Task):
|
||||
|
||||
def __init__(self, job, name, timeoutCount = None):
|
||||
Task.__init__(self, job, name)
|
||||
self.timeoutCount = timeoutCount
|
||||
|
||||
def _run(self):
|
||||
self.triggerCount = 0
|
||||
|
||||
def prepare(self):
|
||||
from enigma import eTimer
|
||||
self.timer = eTimer()
|
||||
self.timer.callback.append(self.trigger)
|
||||
self.timer.start(1000)
|
||||
|
||||
def cleanup(self, failed):
|
||||
if hasattr(self, 'timer'):
|
||||
self.timer.stop()
|
||||
del self.timer
|
||||
|
||||
def check(self):
|
||||
return True
|
||||
|
||||
def trigger(self):
|
||||
self.triggerCount += 1
|
||||
try:
|
||||
if self.timeoutCount is not None and self.triggerCount > self.timeoutCount:
|
||||
raise Exception, 'Timeout elapsed, sorry'
|
||||
res = self.check()
|
||||
except Exception as e:
|
||||
self.postconditions.append(FailedPostcondition(e))
|
||||
res = True
|
||||
|
||||
if res:
|
||||
self.finish()
|
||||
return
|
||||
|
||||
|
||||
class JobManager:
|
||||
|
||||
def __init__(self):
|
||||
self.active_jobs = []
|
||||
self.failed_jobs = []
|
||||
self.job_classes = []
|
||||
self.in_background = False
|
||||
self.visible = False
|
||||
self.active_job = None
|
||||
return
|
||||
|
||||
def AddJob(self, job, onSuccess = None, onFail = None):
|
||||
job.onSuccess = onSuccess
|
||||
if onFail is None:
|
||||
job.onFail = self.notifyFailed
|
||||
else:
|
||||
job.onFail = onFail
|
||||
self.active_jobs.append(job)
|
||||
self.kick()
|
||||
return
|
||||
|
||||
def kick(self):
|
||||
if self.active_job is None:
|
||||
if self.active_jobs:
|
||||
self.active_job = self.active_jobs.pop(0)
|
||||
self.active_job.start(self.jobDone)
|
||||
return
|
||||
|
||||
def notifyFailed(self, job, task, problems):
|
||||
from Tools import Notifications
|
||||
from Screens.MessageBox import MessageBox
|
||||
if problems[0].RECOVERABLE:
|
||||
Notifications.AddNotificationWithCallback(self.errorCB, MessageBox, _('Error: %s\nRetry?') % problems[0].getErrorMessage(task))
|
||||
return True
|
||||
else:
|
||||
Notifications.AddNotification(MessageBox, job.name + '\n' + _('Error') + ': %s' % problems[0].getErrorMessage(task), type=MessageBox.TYPE_ERROR)
|
||||
return False
|
||||
|
||||
def jobDone(self, job, task, problems):
|
||||
print 'job', job, 'completed with', problems, 'in', task
|
||||
if problems:
|
||||
if not job.onFail(job, task, problems):
|
||||
self.errorCB(False)
|
||||
else:
|
||||
self.active_job = None
|
||||
if job.onSuccess:
|
||||
job.onSuccess(job)
|
||||
self.kick()
|
||||
return
|
||||
|
||||
def popupTaskView(self, job):
|
||||
if not self.visible:
|
||||
from Tools import Notifications
|
||||
from Screens.TaskView import JobView
|
||||
self.visible = True
|
||||
Notifications.AddNotification(JobView, job)
|
||||
|
||||
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()
|
||||
return
|
||||
|
||||
def getPendingJobs(self):
|
||||
list = []
|
||||
if self.active_job:
|
||||
list.append(self.active_job)
|
||||
list += self.active_jobs
|
||||
return list
|
||||
|
||||
|
||||
class Condition:
|
||||
RECOVERABLE = False
|
||||
|
||||
def getErrorMessage(self, task):
|
||||
return _('An unknown error occurred!') + ' (%s @ task %s)' % (self.__class__.__name__, task.__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 = 0
|
||||
|
||||
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 disk space. Please free up some disk space 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] == '/':
|
||||
self.realpath = task.cmd
|
||||
print '[Task.py][ToolExistsPrecondition] WARNING: usage of absolute paths for tasks should be avoided!'
|
||||
return os.access(self.realpath, os.X_OK)
|
||||
self.realpath = task.cmd
|
||||
path = os.environ.get('PATH', '').split(os.pathsep)
|
||||
path.append(task.cwd + '/')
|
||||
absolutes = filter(lambda file: os.access(file, os.X_OK), map(lambda directory, file = task.cmd: os.path.join(directory, file), path))
|
||||
if absolutes:
|
||||
self.realpath = absolutes[0]
|
||||
return True
|
||||
return False
|
||||
|
||||
def getErrorMessage(self, task):
|
||||
return _('A required tool (%s) was not found.') % self.realpath
|
||||
|
||||
|
||||
class AbortedPostcondition(Condition):
|
||||
|
||||
def getErrorMessage(self, task):
|
||||
return 'Cancelled upon user request'
|
||||
|
||||
|
||||
class ReturncodePostcondition(Condition):
|
||||
|
||||
def check(self, task):
|
||||
return task.returncode == 0
|
||||
|
||||
def getErrorMessage(self, task):
|
||||
if hasattr(task, 'log') and task.log:
|
||||
log = ''.join(task.log).strip()
|
||||
log = log.split('\n')[-3:]
|
||||
log = '\n'.join(log)
|
||||
return log
|
||||
else:
|
||||
return _('Error code') + ': %s' % task.returncode
|
||||
|
||||
|
||||
class FailedPostcondition(Condition):
|
||||
|
||||
def __init__(self, exception):
|
||||
self.exception = exception
|
||||
|
||||
def getErrorMessage(self, task):
|
||||
if isinstance(self.exception, int):
|
||||
if hasattr(task, 'log'):
|
||||
log = ''.join(task.log).strip()
|
||||
log = log.split('\n')[-4:]
|
||||
log = '\n'.join(log)
|
||||
return log
|
||||
else:
|
||||
return _('Error code') + ' %s' % self.exception
|
||||
return str(self.exception)
|
||||
|
||||
def check(self, task):
|
||||
return self.exception is None or self.exception == 0
|
||||
|
||||
|
||||
job_manager = JobManager()
|
||||
Reference in New Issue
Block a user