Post execute signal and tests (#580)

* adds post_execute signal and tests

* Updates signal docts

* Fixes typo in docs
This commit is contained in:
Ilan Steemers
2021-06-20 15:43:08 +02:00
committed by GitHub
parent 099d4c64c5
commit f8cd136668
4 changed files with 105 additions and 7 deletions
+4 -2
View File
@@ -43,7 +43,7 @@ from django_q.conf import (
from django_q.humanhash import humanize from django_q.humanhash import humanize
from django_q.models import Schedule, Success, Task from django_q.models import Schedule, Success, Task
from django_q.queues import Queue from django_q.queues import Queue
from django_q.signals import pre_execute from django_q.signals import post_execute, pre_execute
from django_q.signing import BadSignature, SignedPackage from django_q.signing import BadSignature, SignedPackage
from django_q.status import Stat, Status from django_q.status import Stat, Status
@@ -386,6 +386,8 @@ def monitor(result_queue: Queue, broker: Broker = None):
ack_id = task.pop("ack_id", False) ack_id = task.pop("ack_id", False)
if ack_id and (task["success"] or task.get("ack_failure", False)): if ack_id and (task["success"] or task.get("ack_failure", False)):
broker.acknowledge(ack_id) broker.acknowledge(ack_id)
# signal execution done
post_execute.send(sender="django_q", task=task)
# log the result # log the result
if task["success"]: if task["success"]:
# log success # log success
@@ -745,7 +747,7 @@ def rss_check():
def localtime() -> datetime: def localtime() -> datetime:
""" Override for timezone.localtime to deal with naive times and local times""" """Override for timezone.localtime to deal with naive times and local times"""
if settings.USE_TZ: if settings.USE_TZ:
return timezone.localtime() return timezone.localtime()
return datetime.now() return datetime.now()
+3
View File
@@ -37,3 +37,6 @@ pre_enqueue = Signal()
# args: func, task # args: func, task
pre_execute = Signal() pre_execute = Signal()
# args: task
post_execute = Signal()
+82
View File
@@ -2,8 +2,10 @@ import os
import sys import sys
import threading import threading
import uuid as uuidlib import uuid as uuidlib
from math import copysign
from multiprocessing import Event, Value from multiprocessing import Event, Value
from time import sleep from time import sleep
from typing import Optional
import pytest import pytest
from django.utils import timezone from django.utils import timezone
@@ -17,6 +19,7 @@ from django_q.conf import Conf
from django_q.humanhash import DEFAULT_WORDLIST, uuid from django_q.humanhash import DEFAULT_WORDLIST, uuid
from django_q.models import Success, Task from django_q.models import Success, Task
from django_q.queues import Queue from django_q.queues import Queue
from django_q.signals import post_execute, pre_enqueue, pre_execute
from django_q.status import Stat from django_q.status import Stat
from django_q.tasks import ( from django_q.tasks import (
async_task, async_task,
@@ -609,6 +612,85 @@ def test_acknowledge_failure_override():
assert broker.acknowledgements.get("test_success_ack_id") == 1 assert broker.acknowledgements.get("test_success_ack_id") == 1
class TestSignals:
@pytest.mark.django_db
def test_pre_enqueue_signal(self, broker):
broker.list_key = "pre_enqueue_test:q"
broker.delete_queue()
self.signal_was_called: bool = False
self.task: Optional[dict] = None
def handler(sender, task, **kwargs):
self.signal_was_called = True
self.task = task
pre_enqueue.connect(handler)
task_id = async_task("math.copysign", 1, -1, broker=broker)
assert self.signal_was_called is True
assert self.task.get("id") == task_id
pre_enqueue.disconnect(handler)
broker.delete_queue()
@pytest.mark.django_db
def test_pre_execute_signal(self, broker):
broker.list_key = "pre_execute_test:q"
broker.delete_queue()
self.signal_was_called: bool = False
self.task: Optional[dict] = None
self.func = None
def handler(sender, task, func, **kwargs):
self.signal_was_called = True
self.task = task
self.func = func
pre_execute.connect(handler)
task_id = async_task("math.copysign", 1, -1, broker=broker)
task_queue = Queue()
result_queue = Queue()
event = Event()
event.set()
pusher(task_queue, event, broker=broker)
task_queue.put("STOP")
worker(task_queue, result_queue, Value("f", -1))
result_queue.put("STOP")
monitor(result_queue, broker)
broker.delete_queue()
assert self.signal_was_called is True
assert self.task.get("id") == task_id
assert self.func == copysign
pre_execute.disconnect(handler)
@pytest.mark.django_db
def test_post_execute_signal(self, broker):
broker.list_key = "post_execute_test:q"
broker.delete_queue()
self.signal_was_called: bool = False
self.task: Optional[dict] = None
self.func = None
def handler(sender, task, **kwargs):
self.signal_was_called = True
self.task = task
post_execute.connect(handler)
task_id = async_task("math.copysign", 1, -1, broker=broker)
task_queue = Queue()
result_queue = Queue()
event = Event()
event.set()
pusher(task_queue, event, broker=broker)
task_queue.put("STOP")
worker(task_queue, result_queue, Value("f", -1))
result_queue.put("STOP")
monitor(result_queue, broker)
broker.delete_queue()
assert self.signal_was_called is True
assert self.task.get("id") == task_id
assert self.task.get("result") == -1
post_execute.disconnect(handler)
@pytest.mark.django_db @pytest.mark.django_db
def assert_result(task): def assert_result(task):
assert task is not None assert task is not None
+16 -5
View File
@@ -24,20 +24,31 @@ executed by a worker. This signal provides two arguments:
with a function path, this argument will be the callable function with a function path, this argument will be the callable function
nonetheless. nonetheless.
After executing a task
""""""""""""""""""""""
The ``django_q.signals.post_execute`` signal is emitted after a task is
executed by a worker and processed by the monitor. It included the ``task`` dictionary with the result.
Subscribing to a signal Subscribing to a signal
----------------------- -----------------------
Connecting to a Django Q signal is done in the same manner as any other Django Connecting to a Django Q signal is done the same as any other Django
signal:: signal::
from django.dispatch import receiver from django.dispatch import receiver
from django_q.signals import pre_enqueue, pre_execute from django_q.signals import pre_enqueue, pre_execute, post_execute
@receiver(pre_enqueue) @receiver(pre_enqueue)
def my_pre_enqueue_callback(sender, task, **kwargs): def my_pre_enqueue_callback(sender, task, **kwargs):
print("Task {} will be enqueued".format(task["name"])) print(f"Task {task['name']} will be queued")
@receiver(pre_execute) @receiver(pre_execute)
def my_pre_execute_callback(sender, func, task, **kwargs): def my_pre_execute_callback(sender, func, task, **kwargs):
print("Task {} will be executed by calling {}".format( print(f"Task {task['name']} will be executed by calling {func}")
task["name"], func))
@receiver(post_execute)
def my_post_execute_callback(sender, task, **kwargs):
print(f"Task {task['name']} was executed with result {task['result']}")