Beispiel #1
0
 def method_test(self):
     """Test if a method can be correctly connected to a signal."""
     signal = Signal()
     foo = FooClass()
     self.assertIsNone(foo.var)
     # connect the signal
     signal.connect(foo.set_var)
     # trigger the signal
     signal.emit("bar")
     # check if the callback triggered correctly
     self.assertEqual(foo.var, "bar")
     # try to trigger the signal again
     signal.emit("baz")
     self.assertEqual(foo.var, "baz")
     # now try to disconnect the signal
     signal.disconnect(foo.set_var)
     # check that calling the signal again
     # no longer triggers the callback
     signal.emit("anaconda")
     self.assertEqual(foo.var, "baz")
Beispiel #2
0
 def lambda_test(self):
     """Test if a lambda can be correctly connected to a signal."""
     foo = FooClass()
     signal = Signal()
     self.assertIsNone(foo.var)
     # connect the signal
     # pylint: disable=unnecessary-lambda
     lambda_instance = lambda x: foo.set_var(x)
     signal.connect(lambda_instance)
     # trigger the signal
     signal.emit("bar")
     # check if the callback triggered correctly
     self.assertEqual(foo.var, "bar")
     # try to trigger the signal again
     signal.emit("baz")
     self.assertEqual(foo.var, "baz")
     # now try to disconnect the signal
     signal.disconnect(lambda_instance)
     # check that calling the signal again
     # no longer triggers the callback
     signal.emit("anaconda")
     self.assertEqual(foo.var, "baz")
Beispiel #3
0
    def function_test(self):
        """Test if a local function can be correctly connected to a signal."""

        # create a local function
        def set_var(value):
            self.var = value

        signal = Signal()
        self.assertIsNone(self.var)
        # connect the signal
        signal.connect(set_var)
        # trigger the signal
        signal.emit("bar")
        # check if the callback triggered correctly
        self.assertEqual(self.var, "bar")
        # try to trigger the signal again
        signal.emit("baz")
        self.assertEqual(self.var, "baz")
        # now try to disconnect the signal
        signal.disconnect(set_var)
        # check that calling the signal again
        # no longer triggers the callback
        signal.emit("anaconda")
        self.assertEqual(self.var, "baz")
Beispiel #4
0
class TaskQueue(BaseTask):
    """TaskQueue represents a queue of TaskQueues or Tasks.

    TaskQueues and Tasks can be mixed in a single TaskQueue.
    """
    def __init__(self, name, status_message=None):
        super(TaskQueue, self).__init__(name=name)
        self._status_message = status_message
        self._current_task_number = None
        self._current_queue_number = None
        # the list backing this TaskQueue instance
        self._list = []
        # triggered if a TaskQueue contained in this one was started/completed
        self.queue_started = Signal()
        self.queue_completed = Signal()
        # triggered when a task is started
        self.task_started = Signal()
        self.task_completed = Signal()

        # connect to the task & queue started signals for
        # progress reporting purposes
        self.queue_started.connect(self._queue_started_cb)
        self.task_started.connect(self._task_started_cb)

    @synchronized
    def _queue_started_cb(self, *args):
        self._current_queue_number += 1

    @synchronized
    def _task_started_cb(self, *args):
        self._current_task_number += 1

    @property
    def status_message(self):
        """A status message describing the Queue is trying to achieve.

        Eq. "Converting all foo into bar."

        The current main usecase is to set the ProgressHub status message when
        a TaskQueue is started.

        :returns: a status message
        :rtype: str
        """
        return self._status_message

    @property
    @synchronized
    def queue_count(self):
        """Returns number of TaskQueues contained in this and all nested TaskQueues.

        :returns: number of queues
        :rtype: int
        """
        queue_count = 0
        for item in self:
            # count only queues
            if isinstance(item, TaskQueue):
                # count the queue itself
                queue_count += 1
                # and its contents
                queue_count += item.queue_count
        return queue_count

    @property
    @synchronized
    def task_count(self):
        """Returns number of tasks contained in this and all nested TaskQueues.

        :returns: number of tasks
        :rtype: int
        """
        task_count = 0
        for item in self:
            if isinstance(item, Task):
                # count tasks
                task_count += 1
            elif isinstance(item, TaskQueue):
                # count tasks in nested queues
                task_count += item.task_count
        return task_count

    @property
    @synchronized
    def current_task_number(self):
        """Number of the currently running task (if any).

        :returns: number of the currently running task (if any)
        :rtype: int or None if no task is currently running
        """
        return self._current_task_number

    @property
    @synchronized
    def current_queue_number(self):
        """Number of the currently running task queue (if any).

        :returns: number of the currently running task queue (if any)
        :rtype: int or None if no task queue is currently running
        """
        return self._current_queue_number

    @property
    @synchronized
    def progress(self):
        """Task queue processing progress.

        The progress is reported as a floating point number from 0.0 to 1.0.
        :returns: task queue processing progress
        :rtype: float
        """
        if self.current_task_number:
            return self.task_count / self.current_task_number
        else:
            return 0.0

    @property
    @synchronized
    def summary(self):
        """Return a multi-line summary of the contents of the task queue.

        :returns: summary of task queue contents
        :rtype: str
        """
        if self.parent is None:
            message = "Top-level task queue: %s\n" % self.name
            # this is the top-level queue, so add some "global" stats
            message += "Number of task queues: %d\n" % self.queue_count
            message += "Number of tasks: %d\n" % self.task_count
            message += "Task & task group listing:\n"
        else:
            message = "Task queue: %s\n" % self.name
        for item in self:
            for line in item.summary.splitlines():
                message += " %s\n" % line
        # remove trailing newlines from the top level message
        if self.parent is None and message[-1] == "\n":
            message = message.rstrip("\n")
        return message

    @property
    @synchronized
    def parent(self):
        """The parent task queue of this task queue (if any).

        :returns: parent of this task queue (if any)
        :rtype: TaskQueue instance or None
        """
        return self._parent

    @parent.setter
    @synchronized
    def parent(self, parent_item):
        # check if a parent is already set
        if self._parent is not None:
            # disconnect from the previous parent first
            self.started.disconnect(self._parent.queue_started.emit)
            self.completed.disconnect(self._parent.queue_completed.emit)
            self.queue_started.disconnect(self._parent.queue_started.emit)
            self.queue_completed.disconnect(self._parent.queue_completed.emit)
            self.task_started.disconnect(self._parent.task_started.emit)
            self.task_completed.disconnect(self._parent.task_completed.emit)
        # set the parent
        self._parent = parent_item
        # Connect own signals "up" to the parent,
        # so that it is possible to monitor how all nested TaskQueues and Tasks
        # are running from the top-level element.

        # connect own start/completion signal to parents queue start/completion signal
        self.started.connect(self._parent.queue_started.emit)
        self.completed.connect(self._parent.queue_completed.emit)

        # propagate start/completion signals from nested queues/tasks
        self.queue_started.connect(self._parent.queue_started.emit)
        self.queue_completed.connect(self._parent.queue_completed.emit)
        self.task_started.connect(self._parent.task_started.emit)
        self.task_completed.connect(self._parent.task_completed.emit)

    def start(self):
        """Start processing of the task queue."""
        do_start = False
        with self._lock:
            # the task queue can only be started once
            if self.running or self.done:
                if self.running:
                    # attempt to start a task that is already running
                    log.error("Can't start task queue %s - already running.")
                else:
                    # attempt to start a task that an already finished task
                    log.error("Can't start task queue %s - already done.")
            else:
                do_start = True
                self._running = True
                if self.task_count:
                    # only set the initial task number if we have some tasks
                    self._current_task_number = 0
                    self._current_queue_number = 0
                else:
                    log.warning(
                        "Attempting to start an empty task queue (%s).",
                        self.name)

        if do_start:
            # go over all task groups and their tasks in order
            self.started.emit(self)
            if len(self) == 0:
                log.warning("The task group %s is empty.", self.name)
            for item in self:
                # start the item (TaskQueue/Task)
                item.start()

            # we are done, set the task queue state accordingly
            with self._lock:
                self._running = False
                self._done = True
                # also set the current task variables accordingly as we no longer process a task
                self._current_task_number = None
                self._current_queue_number = None

            # trigger the "completed" signals
            self.completed.emit(self)

    # implement the Python list "interface" and make sure parent is always
    # set to a correct value
    @synchronized
    def append(self, item):
        item.parent = self
        self._list.append(item)

    @synchronized
    def insert(self, index, item):
        item.parent = self
        self._list.insert(index, item)

    @synchronized
    def __setitem__(self, index, item):
        item.parent = self
        return self._list.__setitem__(index, item)

    @synchronized
    def __len__(self):
        return self._list.__len__()

    @synchronized
    def count(self):
        return self._list.count()

    @synchronized
    def __getitem__(self, ii):
        return self._list[ii]

    @synchronized
    def __delitem__(self, index):
        self._list[index].parent = None
        del self._list[index]

    @synchronized
    def pop(self):
        item = self._list.pop()
        item.parent = None
        return item

    @synchronized
    def clear(self):
        for item in self._list:
            item.parent = None
        self._list.clear()
Beispiel #5
0
class TaskQueue(BaseTask):
    """TaskQueue represents a queue of TaskQueues or Tasks.

    TaskQueues and Tasks can be mixed in a single TaskQueue.
    """

    def __init__(self, name, status_message=None):
        super(TaskQueue, self).__init__(name=name)
        self._status_message = status_message
        self._current_task_number = None
        self._current_queue_number = None
        # the list backing this TaskQueue instance
        self._list = []
        # triggered if a TaskQueue contained in this one was started/completed
        self.queue_started = Signal()
        self.queue_completed = Signal()
        # triggered when a task is started
        self.task_started = Signal()
        self.task_completed = Signal()

        # connect to the task & queue started signals for
        # progress reporting purposes
        self.queue_started.connect(self._queue_started_cb)
        self.task_started.connect(self._task_started_cb)

    @synchronized
    def _queue_started_cb(self, *args):
        self._current_queue_number += 1

    @synchronized
    def _task_started_cb(self, *args):
        self._current_task_number += 1

    @property
    def status_message(self):
        """A status message describing the Queue is trying to achieve.

        Eq. "Converting all foo into bar."

        The current main usecase is to set the ProgressHub status message when
        a TaskQueue is started.

        :returns: a status message
        :rtype: str
        """
        return self._status_message

    @property
    @synchronized
    def queue_count(self):
        """Returns number of TaskQueues contained in this and all nested TaskQueues.

        :returns: number of queues
        :rtype: int
        """
        queue_count = 0
        for item in self:
            # count only queues
            if isinstance(item, TaskQueue):
                # count the queue itself
                queue_count += 1
                # and its contents
                queue_count += item.queue_count
        return queue_count

    @property
    @synchronized
    def task_count(self):
        """Returns number of tasks contained in this and all nested TaskQueues.

        :returns: number of tasks
        :rtype: int
        """
        task_count = 0
        for item in self:
            if isinstance(item, Task):
                # count tasks
                task_count += 1
            elif isinstance(item, TaskQueue):
                # count tasks in nested queues
                task_count += item.task_count
        return task_count

    @property
    @synchronized
    def current_task_number(self):
        """Number of the currently running task (if any).

        :returns: number of the currently running task (if any)
        :rtype: int or None if no task is currently running
        """
        return self._current_task_number

    @property
    @synchronized
    def current_queue_number(self):
        """Number of the currently running task queue (if any).

        :returns: number of the currently running task queue (if any)
        :rtype: int or None if no task queue is currently running
        """
        return self._current_queue_number

    @property
    @synchronized
    def progress(self):
        """Task queue processing progress.

        The progress is reported as a floating point number from 0.0 to 1.0.
        :returns: task queue processing progress
        :rtype: float
        """
        if self.current_task_number:
            return self.task_count / self.current_task_number
        else:
            return 0.0

    @property
    @synchronized
    def summary(self):
        """Return a multi-line summary of the contents of the task queue.

        :returns: summary of task queue contents
        :rtype: str
        """
        if self.parent is None:
            message = "Top-level task queue: %s\n" % self.name
            # this is the top-level queue, so add some "global" stats
            message += "Number of task queues: %d\n" % self.queue_count
            message += "Number of tasks: %d\n" % self.task_count
            message += "Task & task group listing:\n"
        else:
            message = "Task queue: %s\n" % self.name
        for item in self:
            for line in item.summary.splitlines():
                message += " %s\n" % line
        # remove trailing newlines from the top level message
        if self.parent is None and message[-1] == "\n":
            message = message.rstrip("\n")
        return message

    @property
    @synchronized
    def parent(self):
        """The parent task queue of this task queue (if any).

        :returns: parent of this task queue (if any)
        :rtype: TaskQueue instance or None
        """
        return self._parent

    @parent.setter
    @synchronized
    def parent(self, parent_item):
        # check if a parent is already set
        if self._parent is not None:
            # disconnect from the previous parent first
            self.started.disconnect(self._parent.queue_started.emit)
            self.completed.disconnect(self._parent.queue_completed.emit)
            self.queue_started.disconnect(self._parent.queue_started.emit)
            self.queue_completed.disconnect(self._parent.queue_completed.emit)
            self.task_started.disconnect(self._parent.task_started.emit)
            self.task_completed.disconnect(self._parent.task_completed.emit)
        # set the parent
        self._parent = parent_item
        # Connect own signals "up" to the parent,
        # so that it is possible to monitor how all nested TaskQueues and Tasks
        # are running from the top-level element.

        # connect own start/completion signal to parents queue start/completion signal
        self.started.connect(self._parent.queue_started.emit)
        self.completed.connect(self._parent.queue_completed.emit)

        # propagate start/completion signals from nested queues/tasks
        self.queue_started.connect(self._parent.queue_started.emit)
        self.queue_completed.connect(self._parent.queue_completed.emit)
        self.task_started.connect(self._parent.task_started.emit)
        self.task_completed.connect(self._parent.task_completed.emit)

    def start(self):
        """Start processing of the task queue."""
        do_start = False
        with self._lock:
            # the task queue can only be started once
            if self.running or self.done:
                if self.running:
                    # attempt to start a task that is already running
                    log.error("Can't start task queue %s - already running.")
                else:
                    # attempt to start a task that an already finished task
                    log.error("Can't start task queue %s - already done.")
            else:
                do_start = True
                self._running = True
                if self.task_count:
                    # only set the initial task number if we have some tasks
                    self._current_task_number = 0
                    self._current_queue_number = 0
                else:
                    log.warning("Attempting to start an empty task queue (%s).", self.name)

        if do_start:
            # go over all task groups and their tasks in order
            self.started.emit(self)
            if len(self) == 0:
                log.warning("The task group %s is empty.", self.name)
            for item in self:
                # start the item (TaskQueue/Task)
                item.start()

            # we are done, set the task queue state accordingly
            with self._lock:
                self._running = False
                self._done = True
                # also set the current task variables accordingly as we no longer process a task
                self._current_task_number = None
                self._current_queue_number = None

            # trigger the "completed" signals
            self.completed.emit(self)

    # implement the Python list "interface" and make sure parent is always
    # set to a correct value
    @synchronized
    def append(self, item):
        item.parent = self
        self._list.append(item)

    @synchronized
    def insert(self, index, item):
        item.parent = self
        self._list.insert(index, item)

    @synchronized
    def __setitem__(self, index, item):
        item.parent = self
        return self._list.__setitem__(index, item)

    @synchronized
    def __len__(self):
        return self._list.__len__()

    @synchronized
    def count(self):
        return self._list.count()

    @synchronized
    def __getitem__(self, ii):
        return self._list[ii]

    @synchronized
    def __delitem__(self, index):
        self._list[index].parent = None
        del self._list[index]

    @synchronized
    def pop(self):
        item = self._list.pop()
        item.parent = None
        return item

    @synchronized
    def clear(self):
        for item in self._list:
            item.parent = None
        self._list.clear()