Skip to content

Commit 71f53be

Browse files
committed
Add pre and post task hooks.
1 parent 9a8facd commit 71f53be

File tree

6 files changed

+114
-1
lines changed

6 files changed

+114
-1
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,29 @@ JOBS = {
112112
}
113113
```
114114

115+
#### Pre & Post Task Hooks
116+
You can also run pre task or post task hooks, which happen in the normal processing of your `Job` instances and are executed in the worker process.
117+
118+
Both pre and post task hooks receive your `Job` instance as their only argument. Here's an example:
119+
120+
```python
121+
def my_pre_task_hook(job):
122+
... # configure something before running your task
123+
```
124+
125+
To ensure these hooks gets run, simply add a `pre_task_hook` or `post_task_hook` key (or both, if needed) to your job config like so:
126+
127+
```python
128+
JOBS = {
129+
"my_job": {
130+
"tasks": ["project.common.jobs.my_task"],
131+
"pre_task_hook": "project.common.jobs.my_pre_task_hook",
132+
"post_task_hook": "project.common.jobs.my_post_task_hook",
133+
},
134+
}
135+
```
136+
137+
115138
### Start the worker
116139

117140
In another terminal:

django_dbq/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "3.1.0"
1+
__version__ = "3.2.0"

django_dbq/management/commands/worker.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ def _process_job(self):
6161
if not job:
6262
return
6363

64+
job.run_pre_task_hook()
65+
6466
logger.info(
6567
'Processing job: name="%s" queue="%s" id=%s state=%s next_task=%s',
6668
job.name,
@@ -109,6 +111,8 @@ def _process_job(self):
109111
logger.exception("Failed to save job: id=%s", job.pk)
110112
raise
111113

114+
job.run_post_task_hook()
115+
112116
self.current_job = None
113117

114118

django_dbq/models.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from django.utils.module_loading import import_string
44
from django_dbq.tasks import (
55
get_next_task_name,
6+
get_pre_task_hook_name,
7+
get_post_task_hook_name,
68
get_failure_hook_name,
79
get_creation_hook_name,
810
)
@@ -126,12 +128,32 @@ def save(self, *args, **kwargs):
126128
def update_next_task(self):
127129
self.next_task = get_next_task_name(self.name, self.next_task) or ""
128130

131+
def get_pre_task_hook_name(self):
132+
return get_pre_task_hook_name(self.name)
133+
134+
def get_post_task_hook_name(self):
135+
return get_post_task_hook_name(self.name)
136+
129137
def get_failure_hook_name(self):
130138
return get_failure_hook_name(self.name)
131139

132140
def get_creation_hook_name(self):
133141
return get_creation_hook_name(self.name)
134142

143+
def run_pre_task_hook(self):
144+
pre_task_hook_name = self.get_pre_task_hook_name()
145+
if pre_task_hook_name:
146+
logger.info("Running pre_task hook %s for new job", pre_task_hook_name)
147+
pre_task_hook_function = import_string(pre_task_hook_name)
148+
pre_task_hook_function(self)
149+
150+
def run_post_task_hook(self):
151+
post_task_hook_name = self.get_post_task_hook_name()
152+
if post_task_hook_name:
153+
logger.info("Running post_task hook %s for new job", post_task_hook_name)
154+
post_task_hook_function = import_string(post_task_hook_name)
155+
post_task_hook_function(self)
156+
135157
def run_creation_hook(self):
136158
creation_hook_name = self.get_creation_hook_name()
137159
if creation_hook_name:

django_dbq/tasks.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33

44
TASK_LIST_KEY = "tasks"
5+
PRE_TASK_HOOK_KEY = "pre_task_hook"
6+
POST_TASK_HOOK_KEY = "post_task_hook"
57
FAILURE_HOOK_KEY = "failure_hook"
68
CREATION_HOOK_KEY = "creation_hook"
79

@@ -24,6 +26,16 @@ def get_next_task_name(job_name, current_task=None):
2426
return None
2527

2628

29+
def get_pre_task_hook_name(job_name):
30+
"""Return the name of the pre task hook for the given job (as a string) or None"""
31+
return settings.JOBS[job_name].get(PRE_TASK_HOOK_KEY)
32+
33+
34+
def get_post_task_hook_name(job_name):
35+
"""Return the name of the post_task hook for the given job (as a string) or None"""
36+
return settings.JOBS[job_name].get(POST_TASK_HOOK_KEY)
37+
38+
2739
def get_failure_hook_name(job_name):
2840
"""Return the name of the failure hook for the given job (as a string) or None"""
2941
return settings.JOBS[job_name].get(FAILURE_HOOK_KEY)

django_dbq/tests.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,25 @@ def failing_task(job):
3434
raise Exception("uh oh")
3535

3636

37+
def pre_task_hook(job):
38+
job.workspace["output"] = "pre task hook ran"
39+
job.workspace["job_id"] = str(job.id)
40+
41+
42+
def post_task_hook(job):
43+
job.workspace["output"] = "post task hook ran"
44+
job.workspace["job_id"] = str(job.id)
45+
46+
3747
def failure_hook(job, exception):
3848
job.workspace["output"] = "failure hook ran"
49+
job.workspace["exception"] = str(exception)
50+
job.workspace["job_id"] = str(job.id)
3951

4052

4153
def creation_hook(job):
4254
job.workspace["output"] = "creation hook ran"
55+
job.workspace["job_id"] = str(job.id)
4356

4457

4558
@override_settings(JOBS={"testjob": {"tasks": ["a"]}})
@@ -316,6 +329,7 @@ def test_creation_hook(self):
316329
job = Job.objects.create(name="testjob")
317330
job = Job.objects.get()
318331
self.assertEqual(job.workspace["output"], "creation hook ran")
332+
self.assertEqual(job.workspace["job_id"], str(job.id))
319333

320334
def test_creation_hook_only_runs_on_create(self):
321335
job = Job.objects.create(name="testjob")
@@ -326,6 +340,42 @@ def test_creation_hook_only_runs_on_create(self):
326340
self.assertEqual(job.workspace["output"], "creation hook output removed")
327341

328342

343+
@override_settings(
344+
JOBS={
345+
"testjob": {
346+
"tasks": ["django_dbq.tests.failing_task"],
347+
"pre_task_hook": "django_dbq.tests.pre_task_hook",
348+
}
349+
}
350+
)
351+
class JobPreTaskHookTestCase(TestCase):
352+
def test_pre_task_hook(self):
353+
job = Job.objects.create(name="testjob")
354+
Worker("default", 1)._process_job()
355+
job = Job.objects.get()
356+
self.assertEqual(job.state, Job.STATES.FAILED)
357+
self.assertEqual(job.workspace["output"], "failure hook ran")
358+
self.assertEqual(job.workspace["job_id"], str(job.id))
359+
360+
361+
@override_settings(
362+
JOBS={
363+
"testjob": {
364+
"tasks": ["django_dbq.tests.failing_task"],
365+
"post_task_hook": "django_dbq.tests.post_task_hook",
366+
}
367+
}
368+
)
369+
class JobPostTaskHookTestCase(TestCase):
370+
def test_post_task_hook(self):
371+
job = Job.objects.create(name="testjob")
372+
Worker("default", 1)._process_job()
373+
job = Job.objects.get()
374+
self.assertEqual(job.state, Job.STATES.FAILED)
375+
self.assertEqual(job.workspace["output"], "post task hook ran")
376+
self.assertEqual(job.workspace["job_id"], str(job.id))
377+
378+
329379
@override_settings(
330380
JOBS={
331381
"testjob": {
@@ -341,6 +391,8 @@ def test_failure_hook(self):
341391
job = Job.objects.get()
342392
self.assertEqual(job.state, Job.STATES.FAILED)
343393
self.assertEqual(job.workspace["output"], "failure hook ran")
394+
self.assertIn("uh oh", job.workspace["exception"])
395+
self.assertEqual(job.workspace["job_id"], str(job.id))
344396

345397

346398
@override_settings(JOBS={"testjob": {"tasks": ["a"]}})

0 commit comments

Comments
 (0)