Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion background_task/management/commands/process_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@

from background_task.tasks import tasks, autodiscover
from background_task.utils import SignalManager
from compat import close_connection
from django.db import connection

def close_connection():
connection.close()


logger = logging.getLogger(__name__)
Expand Down
6 changes: 2 additions & 4 deletions background_task/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
import os
import traceback

from compat import StringIO
from compat.models import GenericForeignKey
from io import StringIO
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import Q
from django.utils import timezone
from django.utils.six import python_2_unicode_compatible

from background_task.exceptions import InvalidTaskError
from background_task.settings import app_settings
Expand Down Expand Up @@ -128,7 +127,6 @@ def drop_task(self, task_name, args=None, kwargs=None):
return self.get_task(task_name, args, kwargs).delete()


@python_2_unicode_compatible
class Task(models.Model):
# the "name" of the task/function to be run
task_name = models.CharField(max_length=190, db_index=True)
Expand Down
4 changes: 1 addition & 3 deletions background_task/models_completed.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
import os

from compat.models import GenericForeignKey
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

from django.db import models
from django.utils import timezone
from django.utils.six import python_2_unicode_compatible

from background_task.models import Task

Expand Down Expand Up @@ -51,7 +50,6 @@ def succeeded(self, within=None):
return qs


@python_2_unicode_compatible
class CompletedTask(models.Model):
# the "name" of the task/function to be run
task_name = models.CharField(max_length=190, db_index=True)
Expand Down
10 changes: 5 additions & 5 deletions background_task/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
from django.db import connections
from background_task.settings import app_settings

task_created = django.dispatch.Signal(providing_args=['task'])
task_error = django.dispatch.Signal(providing_args=['task'])
task_rescheduled = django.dispatch.Signal(providing_args=['task'])
task_failed = django.dispatch.Signal(providing_args=['task_id', 'completed_task'])
task_successful = django.dispatch.Signal(providing_args=['task_id', 'completed_task'])
task_created = django.dispatch.Signal()
task_error = django.dispatch.Signal()
task_rescheduled = django.dispatch.Signal()
task_failed = django.dispatch.Signal()
task_successful = django.dispatch.Signal()
task_started = django.dispatch.Signal()
task_finished = django.dispatch.Signal()

Expand Down
12 changes: 5 additions & 7 deletions background_task/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

from django.db.utils import OperationalError
from django.utils import timezone
from django.utils.six import python_2_unicode_compatible

from background_task.exceptions import BackgroundTaskError
from background_task.models import Task
Expand Down Expand Up @@ -266,7 +265,6 @@ def run_next_task(self, tasks, queue=None):
return False


@python_2_unicode_compatible
class TaskProxy(object):
def __init__(self, name, task_function, schedule, queue, remove_existing_tasks, runner):
self.name = name
Expand Down Expand Up @@ -305,17 +303,17 @@ def autodiscover():
"""
Autodiscover tasks.py files in much the same way as admin app
"""
import imp
import importlib
from django.conf import settings

for app in settings.INSTALLED_APPS:
try:
app_path = import_module(app).__path__
except (AttributeError, ImportError):
continue
try:
imp.find_module('tasks', app_path)
except ImportError:

# Check if tasks module exists
if importlib.util.find_spec(f"{app}.tasks") is None:
continue

import_module("%s.tasks" % app)
import_module(f"{app}.tasks")
96 changes: 48 additions & 48 deletions background_task/tests/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def test_str(self):
def test_shortcut(self):
'''check shortcut to decorator works'''
proxy = background()(empty_task)
self.failIfEqual(proxy, empty_task)
self.assertNotEqual(proxy, empty_task)
self.assertEqual(proxy.task_function, empty_task)

def test_launch_sync(self):
Expand Down Expand Up @@ -151,9 +151,9 @@ def test_priority(self):
self.assertEqual(2, TaskSchedule(priority=2).priority)

def _within_one_second(self, d1, d2):
self.failUnless(isinstance(d1, datetime))
self.failUnless(isinstance(d2, datetime))
self.failUnless(abs(d1 - d2) <= timedelta(seconds=1))
self.assertTrue(isinstance(d1, datetime))
self.assertTrue(isinstance(d2, datetime))
self.assertTrue(abs(d1 - d2) <= timedelta(seconds=1))

def test_run_at(self):
for schedule in [None, 0, timedelta(seconds=0)]:
Expand Down Expand Up @@ -268,8 +268,8 @@ def reschedule_fn():

# check task is scheduled for later on
now = timezone.now()
self.failUnless(now + timedelta(seconds=89) < task.run_at)
self.failUnless(now + timedelta(seconds=91) > task.run_at)
self.assertTrue(now + timedelta(seconds=89) < task.run_at)
self.assertTrue(now + timedelta(seconds=91) > task.run_at)

def test_check_existing(self):

Expand All @@ -292,8 +292,8 @@ def check_fn():

# check new task is scheduled for the earlier time
now = timezone.now()
self.failUnless(now - timedelta(seconds=1) < task.run_at)
self.failUnless(now + timedelta(seconds=1) > task.run_at)
self.assertTrue(now - timedelta(seconds=1) < task.run_at)
self.assertTrue(now + timedelta(seconds=1) > task.run_at)


class TestTaskRunner(TransactionTestCase):
Expand All @@ -303,19 +303,19 @@ def setUp(self):
self.runner = tasks._runner

def test_get_task_to_run_no_tasks(self):
self.failIf(self.runner.get_task_to_run(tasks))
self.assertFalse(self.runner.get_task_to_run(tasks))

def test_get_task_to_run(self):
task = Task.objects.new_task('mytask', (1), {})
task.save()
self.failUnless(task.locked_by is None)
self.failUnless(task.locked_at is None)
self.assertTrue(task.locked_by is None)
self.assertTrue(task.locked_at is None)

locked_task = self.runner.get_task_to_run(tasks)
self.failIf(locked_task is None)
self.failIf(locked_task.locked_by is None)
self.assertFalse(locked_task is None)
self.assertFalse(locked_task.locked_by is None)
self.assertEqual(self.runner.worker_name, locked_task.locked_by)
self.failIf(locked_task.locked_at is None)
self.assertFalse(locked_task.locked_at is None)
self.assertEqual('mytask', locked_task.task_name)


Expand All @@ -324,22 +324,22 @@ class TestTaskModel(TransactionTestCase):
def test_lock_uncontested(self):
task = Task.objects.new_task('mytask')
task.save()
self.failUnless(task.locked_by is None)
self.failUnless(task.locked_at is None)
self.assertTrue(task.locked_by is None)
self.assertTrue(task.locked_at is None)

locked_task = task.lock('mylock')
self.assertEqual('mylock', locked_task.locked_by)
self.failIf(locked_task.locked_at is None)
self.assertFalse(locked_task.locked_at is None)
self.assertEqual(task.pk, locked_task.pk)

def test_lock_contested(self):
# locking should actually look at db, not object
# in memory
task = Task.objects.new_task('mytask')
task.save()
self.failIf(task.lock('mylock') is None)
self.assertFalse(task.lock('mylock') is None)

self.failUnless(task.lock('otherlock') is None)
self.assertTrue(task.lock('otherlock') is None)

def test_lock_expired(self):
task = Task.objects.new_task('mytask')
Expand All @@ -352,7 +352,7 @@ def test_lock_expired(self):
locked_task.save()

# now try to get the lock again
self.failIf(task.lock('otherlock') is None)
self.assertFalse(task.lock('otherlock') is None)

def test_str(self):
task = Task.objects.new_task('mytask')
Expand Down Expand Up @@ -413,29 +413,29 @@ def throws_error():
self.throws_error = throws_error

def test_run_next_task_nothing_scheduled(self):
self.failIf(run_next_task())
self.assertFalse(run_next_task())

def test_run_next_task_one_task_scheduled(self):
self.set_fields(worked=True)
self.failIf(hasattr(self, 'worked'))
self.assertFalse(hasattr(self, 'worked'))

self.failUnless(run_next_task())
self.assertTrue(run_next_task())

self.failUnless(hasattr(self, 'worked'))
self.failUnless(self.worked)
self.assertTrue(hasattr(self, 'worked'))
self.assertTrue(self.worked)

def test_run_next_task_several_tasks_scheduled(self):
self.set_fields(one='1')
self.set_fields(two='2')
self.set_fields(three='3')

for i in range(3):
self.failUnless(run_next_task())
self.assertTrue(run_next_task())

self.failIf(run_next_task()) # everything should have been run
self.assertFalse(run_next_task()) # everything should have been run

for field, value in [('one', '1'), ('two', '2'), ('three', '3')]:
self.failUnless(hasattr(self, field))
self.assertTrue(hasattr(self, field))
self.assertEqual(value, getattr(self, field))

def test_run_next_task_error_handling(self):
Expand All @@ -446,35 +446,35 @@ def test_run_next_task_error_handling(self):
original_task = all_tasks[0]

# should run, but trigger error
self.failUnless(run_next_task())
self.assertTrue(run_next_task())

all_tasks = Task.objects.all()
self.assertEqual(1, all_tasks.count())

failed_task = all_tasks[0]
# should have an error recorded
self.failIfEqual('', failed_task.last_error)
self.failUnless(failed_task.failed_at is None)
self.assertNotEqual('', failed_task.last_error)
self.assertTrue(failed_task.failed_at is None)
self.assertEqual(1, failed_task.attempts)

# should have been rescheduled for the future
# and no longer locked
self.failUnless(failed_task.run_at > original_task.run_at)
self.failUnless(failed_task.locked_by is None)
self.failUnless(failed_task.locked_at is None)
self.assertTrue(failed_task.run_at > original_task.run_at)
self.assertTrue(failed_task.locked_by is None)
self.assertTrue(failed_task.locked_at is None)

def test_run_next_task_does_not_run_locked(self):
self.set_fields(locked=True)
self.failIf(hasattr(self, 'locked'))
self.assertFalse(hasattr(self, 'locked'))

all_tasks = Task.objects.all()
self.assertEqual(1, all_tasks.count())
original_task = all_tasks[0]
original_task.lock('lockname')

self.failIf(run_next_task())
self.assertFalse(run_next_task())

self.failIf(hasattr(self, 'locked'))
self.assertFalse(hasattr(self, 'locked'))
all_tasks = Task.objects.all()
self.assertEqual(1, all_tasks.count())

Expand All @@ -486,9 +486,9 @@ def test_run_next_task_unlocks_after_MAX_RUN_TIME(self):
original_task = all_tasks[0]
locked_task = original_task.lock('lockname')

self.failIf(run_next_task())
self.assertFalse(run_next_task())

self.failIf(hasattr(self, 'lock_overridden'))
self.assertFalse(hasattr(self, 'lock_overridden'))

# put lot time into past
expire_by = timedelta(seconds=(app_settings.BACKGROUND_TASK_MAX_RUN_TIME + 2))
Expand All @@ -497,11 +497,11 @@ def test_run_next_task_unlocks_after_MAX_RUN_TIME(self):

# so now we should be able to override the lock
# and run the task
self.failUnless(run_next_task())
self.assertTrue(run_next_task())
self.assertEqual(0, Task.objects.count())

self.failUnless(hasattr(self, 'lock_overridden'))
self.failUnless(self.lock_overridden)
self.assertTrue(hasattr(self, 'lock_overridden'))
self.assertTrue(self.lock_overridden)

def test_default_schedule_used_for_run_at(self):

Expand All @@ -516,9 +516,9 @@ def default_schedule_used_for_time():
self.assertEqual(1, all_tasks.count())
task = all_tasks[0]

self.failUnless(now < task.run_at)
self.failUnless((task.run_at - now) <= timedelta(seconds=61))
self.failUnless((task.run_at - now) >= timedelta(seconds=59))
self.assertTrue(now < task.run_at)
self.assertTrue((task.run_at - now) <= timedelta(seconds=61))
self.assertTrue((task.run_at - now) >= timedelta(seconds=59))

def test_default_schedule_used_for_priority(self):

Expand Down Expand Up @@ -561,14 +561,14 @@ def failed_at_set_after_MAX_ATTEMPTS():
self.assertEqual(1, available.count())
task = available[0]

self.failUnless(task.failed_at is None)
self.assertTrue(task.failed_at is None)

task.attempts = app_settings.BACKGROUND_TASK_MAX_ATTEMPTS
task.save()

# task should be scheduled to run now
# but will be marked as failed straight away
self.failUnless(run_next_task())
self.assertTrue(run_next_task())

available = Task.objects.find_available()
self.assertEqual(0, available.count())
Expand All @@ -577,7 +577,7 @@ def failed_at_set_after_MAX_ATTEMPTS():
self.assertEqual(0, all_tasks.count())
self.assertEqual(1, CompletedTask.objects.count())
completed_task = CompletedTask.objects.all()[0]
self.failIf(completed_task.failed_at is None)
self.assertFalse(completed_task.failed_at is None)

def test_run_task_return_value(self):
return_value = self.set_fields(test='test')
Expand Down
3 changes: 1 addition & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
django-compat>=1.0.13
six

7 changes: 2 additions & 5 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
[tox]
envlist =
py{27}-django{18,111}-{sync,async}
py{34}-django{18,111,20}-{sync,async}
py{35}-django{18,111,20,21}-{sync,async}
py{36}-django{111,20,21}-{sync,async}
py{37}-django{20,21}-{sync,async}

py{312}-django{52}-{sync,async}
[testenv]
deps =
coverage
Expand All @@ -14,6 +10,7 @@ deps =
django111: Django>=1.11,<1.12
django20: Django>=2.0,<2.1
django21: Django>=2.1,<2.2
django52: Django>=5.2,<6
-r{toxinidir}/requirements-test.txt
-r{toxinidir}/requirements.txt

Expand Down