def __init__ (self, _adaptor, _method_type, _method_context, _ttype) : """ This saga.Task constructor is private. ``_adaptor`` references the adaptor class instance from which this task was created via an asynchronous function. Note that the API level object instance can be inferred via ``_adaptor.get_api ()``. Further, the adaptor will reference an _adaptor._container class, which will be considered the target for bulk operations for this task. ``_method_type`` specifies the SAGA API method which task is representing. For example, for the following code:: d = saga.filesystem.Directory ("file:///") t = d.copy ('/etc/passwd', '/tmp/passwd.bak', saga.task.ASYNC) The resulting task ``t`` would represent the *'copy'* method. This is required to forward :class:`saga.task.Container` calls to the correct bulk method, in this case ``container_copy()``. ``_method_context`` describes the context in which the task method is running. It is up to the creator of the task to provide that context -- in general, it will at least include method parameters. ``ttype`` determines in what state the constructor will leave the task: ``DONE`` for ``ttype=SYNC``, ``RUNNING`` for ``ttype=ASYNC`` and ``NEW`` for ``ttype=TASK``. If the ``_method_context`` has *exactly* two elements, names ``_call`` and ``args``, then the created task will wrap a :class:`saga.util.threads.Thread` with that ``_call (_args)``. """ self._base = super (Task, self) self._base.__init__ () self._thread = None self._ttype = _ttype self._adaptor = _adaptor self._method_type = _method_type self._method_context = _method_context # set attribute interface properties self._attributes_allow_private (True) self._attributes_extensible (False) self._attributes_camelcasing (True) # register properties with the attribute interface self._attributes_register (RESULT, None, satt.ANY, satt.SCALAR, satt.READONLY) self._attributes_set_getter (RESULT, self.get_result) self._attributes_set_setter (RESULT, self._set_result) self._attributes_register (EXCEPTION, None, satt.ANY, satt.SCALAR, satt.READONLY) self._attributes_set_getter (EXCEPTION, self.get_exception) self._attributes_set_setter (EXCEPTION, self._set_exception) self._attributes_register (STATE, UNKNOWN, satt.ENUM, satt.SCALAR, satt.READONLY) self._attributes_set_enums (STATE, [UNKNOWN, NEW, RUNNING, DONE, FAILED, CANCELED]) self._attributes_set_getter (STATE, self.get_state) self._attributes_set_setter (STATE, self._set_state) self._set_state (NEW) # check if this task is supposed to wrap a callable in a thread if '_call' in self._method_context : if not '_args' in self._method_context : self._method_context['_args'] = () if not '_kwargs' in self._method_context : self._method_context['_kwargs'] = {} if 3 != len (self._method_context) : raise se.BadParameter ("invalid call context for callable task") call = self._method_context['_call'] args = self._method_context['_args'] kwargs = self._method_context['_kwargs'] self._thread = Thread (call, *args, **kwargs) # ensure task goes into the correct state if self._ttype == SYNC : self.run () self.wait () elif self._ttype == ASYNC : self.run () elif self._ttype == TASK : pass
class Task (sbase.SimpleBase, satt.Attributes) : # -------------------------------------------------------------------------- # @sus.takes ('Task', sab.Base, basestring, dict, sus.one_of (SYNC, ASYNC, TASK)) @sus.returns (sus.nothing) def __init__ (self, _adaptor, _method_type, _method_context, _ttype) : """ This saga.Task constructor is private. ``_adaptor`` references the adaptor class instance from which this task was created via an asynchronous function. Note that the API level object instance can be inferred via ``_adaptor.get_api ()``. Further, the adaptor will reference an _adaptor._container class, which will be considered the target for bulk operations for this task. ``_method_type`` specifies the SAGA API method which task is representing. For example, for the following code:: d = saga.filesystem.Directory ("file:///") t = d.copy ('/etc/passwd', '/tmp/passwd.bak', saga.task.ASYNC) The resulting task ``t`` would represent the *'copy'* method. This is required to forward :class:`saga.task.Container` calls to the correct bulk method, in this case ``container_copy()``. ``_method_context`` describes the context in which the task method is running. It is up to the creator of the task to provide that context -- in general, it will at least include method parameters. ``ttype`` determines in what state the constructor will leave the task: ``DONE`` for ``ttype=SYNC``, ``RUNNING`` for ``ttype=ASYNC`` and ``NEW`` for ``ttype=TASK``. If the ``_method_context`` has *exactly* two elements, names ``_call`` and ``args``, then the created task will wrap a :class:`saga.util.threads.Thread` with that ``_call (_args)``. """ self._base = super (Task, self) self._base.__init__ () self._thread = None self._ttype = _ttype self._adaptor = _adaptor self._method_type = _method_type self._method_context = _method_context # set attribute interface properties self._attributes_allow_private (True) self._attributes_extensible (False) self._attributes_camelcasing (True) # register properties with the attribute interface self._attributes_register (RESULT, None, satt.ANY, satt.SCALAR, satt.READONLY) self._attributes_set_getter (RESULT, self.get_result) self._attributes_set_setter (RESULT, self._set_result) self._attributes_register (EXCEPTION, None, satt.ANY, satt.SCALAR, satt.READONLY) self._attributes_set_getter (EXCEPTION, self.get_exception) self._attributes_set_setter (EXCEPTION, self._set_exception) self._attributes_register (STATE, UNKNOWN, satt.ENUM, satt.SCALAR, satt.READONLY) self._attributes_set_enums (STATE, [UNKNOWN, NEW, RUNNING, DONE, FAILED, CANCELED]) self._attributes_set_getter (STATE, self.get_state) self._attributes_set_setter (STATE, self._set_state) self._set_state (NEW) # check if this task is supposed to wrap a callable in a thread if '_call' in self._method_context : if not '_args' in self._method_context : self._method_context['_args'] = () if not '_kwargs' in self._method_context : self._method_context['_kwargs'] = {} if 3 != len (self._method_context) : raise se.BadParameter ("invalid call context for callable task") call = self._method_context['_call'] args = self._method_context['_args'] kwargs = self._method_context['_kwargs'] self._thread = Thread (call, *args, **kwargs) # ensure task goes into the correct state if self._ttype == SYNC : self.run () self.wait () elif self._ttype == ASYNC : self.run () elif self._ttype == TASK : pass # -------------------------------------------------------------------------- # @sus.takes ('Task') @sus.returns (sus.nothing) def run (self) : if self._thread : self._thread.run () else : # FIXME: make sure task_run exists. Should be part of the CPI! self._adaptor.task_run (self) # -------------------------------------------------------------------------- # @sus.takes ('Task', float) @sus.returns (sus.nothing) def wait (self, timeout=None) : if None == timeout : timeout = -1.0 # FIXME if self._thread : self._thread.wait () # FIXME: timeout?! self._set_state (self._thread.state) else : # FIXME: make sure task_wait exists. Should be part of the CPI! self._adaptor.task_wait (self, timeout) # ---------------------------------------------------------------- # @sus.takes ('Task') @sus.returns (sus.nothing) def cancel (self) : if self._thread : self._thread.cancel () self._set_state (CANCELED) else : # FIXME: make sure task_cancel exists. Should be part of the CPI! self._adaptor.task_cancel (self) # -------------------------------------------------------------------------- # @sus.takes ('Task', sus.one_of (UNKNOWN, NEW, RUNNING, DONE, FAILED, CANCELED)) @sus.returns (sus.nothing) def _set_state (self, state) : if not state in [UNKNOWN, NEW, RUNNING, DONE, FAILED, CANCELED] : raise se.BadParameter ("attempt to set invalid task state '%s'" % state) self._attributes_i_set (self._attributes_t_underscore (STATE), state, force=True) # -------------------------------------------------------------------------- # @sus.takes ('Task') @sus.returns (sus.one_of (UNKNOWN, NEW, RUNNING, DONE, FAILED, CANCELED)) def get_state (self) : if self._thread : self._set_state (self._thread.state) return self.state # -------------------------------------------------------------------------- # @sus.takes ('Task', sus.anything) @sus.returns (sus.nothing) def _set_result (self, result) : self._attributes_i_set (self._attributes_t_underscore (RESULT), result, force=True) self._attributes_i_set (self._attributes_t_underscore (STATE), DONE, force=True) # -------------------------------------------------------------------------- # @sus.takes ('Task') @sus.returns (sus.anything) def get_result (self) : if not self.state in [DONE, FAILED, CANCELED] : self.wait () assert (self.state in [DONE, FAILED, CANCELED]) if self.state == FAILED : self.re_raise () return if self.state == CANCELED : raise se.IncorrectState ("task.get_result() cannot be called on cancelled tasks") if self.state == DONE : if self._thread : self._set_result (self._thread.result) return self.result # -------------------------------------------------------------------------- # @sus.takes ('Task', se.SagaException) @sus.returns (sus.nothing) def _set_exception (self, e) : self._attributes_i_set (self._attributes_t_underscore (EXCEPTION), e, force=True) # -------------------------------------------------------------------------- # @sus.takes ('Task') @sus.returns (se.SagaException) def get_exception (self) : if self._thread : self._set_exception (self._thread.exception) return self.exception # -------------------------------------------------------------------------- # @sus.takes ('Task') @sus.returns (sus.nothing) def re_raise (self) : if self.exception : raise self.exception