Skip to content

Commit 0dbdc5d

Browse files
author
Ask Solem
committed
94% total coverage
1 parent 4890e0f commit 0dbdc5d

File tree

12 files changed

+372
-70
lines changed

12 files changed

+372
-70
lines changed

celery/backends/cache.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,40 @@
66
from celery.backends.base import KeyValueStoreBackend
77
from celery.exceptions import ImproperlyConfigured
88
from celery.utils import timeutils
9+
from celery.datastructures import LocalCache
910

10-
try:
11-
import pylibmc as memcache
12-
except ImportError:
11+
12+
13+
def get_best_memcache(*args, **kwargs):
1314
try:
14-
import memcache
15+
import pylibmc as memcache
1516
except ImportError:
16-
raise ImproperlyConfigured("Memcached backend requires either "
17-
"the 'memcache' or 'pylibmc' library")
17+
try:
18+
import memcache
19+
except ImportError:
20+
raise ImproperlyConfigured("Memcached backend requires either "
21+
"the 'memcache' or 'pylibmc' library")
22+
return memcache.Client(*args, **kwargs)
1823

1924

20-
class CacheBackend(KeyValueStoreBackend):
21-
Client = memcache.Client
25+
class DummyClient(object):
26+
27+
def __init__(self, *args, **kwargs):
28+
self.cache = LocalCache(5000)
29+
30+
def get(self, key, *args, **kwargs):
31+
return self.cache.get(key)
2232

33+
def set(self, key, value, *args, **kwargs):
34+
self.cache[key] = value
35+
36+
37+
backends = {"memcache": get_best_memcache,
38+
"memcached": get_best_memcache,
39+
"pylibmc": get_best_memcache,
40+
"memory": DummyClient}
41+
42+
class CacheBackend(KeyValueStoreBackend):
2343
_client = None
2444

2545
def __init__(self, expires=conf.TASK_RESULT_EXPIRES,
@@ -31,6 +51,14 @@ def __init__(self, expires=conf.TASK_RESULT_EXPIRES,
3151
self.options = dict(conf.CACHE_BACKEND_OPTIONS, **options)
3252
self.backend, _, servers = partition(backend, "://")
3353
self.servers = servers.split(";")
54+
try:
55+
self.Client = backends[self.backend]
56+
except KeyError:
57+
raise ImproperlyConfigured(
58+
"Unknown cache backend: %s. Please use one of the "
59+
"following backends: %s" % (self.backend,
60+
", ".join(backends.keys())))
61+
3462

3563
def get(self, key):
3664
return self.client.get(key)

celery/backends/database.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,6 @@ def _store_result(self, task_id, result, status, traceback=None):
4444
session.close()
4545
return result
4646

47-
def _save_taskset(self, taskset_id, result):
48-
"""Store the result of an executed taskset."""
49-
taskset = TaskSet(taskset_id, result)
50-
session = self.ResultSession()
51-
try:
52-
session.add(taskset)
53-
session.flush()
54-
session.commit()
55-
finally:
56-
session.close()
57-
return result
58-
5947
def _get_task_meta_for(self, task_id):
6048
"""Get task metadata for a task by id."""
6149
session = self.ResultSession()
@@ -66,17 +54,28 @@ def _get_task_meta_for(self, task_id):
6654
session.add(task)
6755
session.flush()
6856
session.commit()
69-
if task:
70-
return task.to_dict()
57+
return task.to_dict()
58+
finally:
59+
session.close()
60+
61+
def _save_taskset(self, taskset_id, result):
62+
"""Store the result of an executed taskset."""
63+
session = self.ResultSession()
64+
try:
65+
taskset = TaskSet(taskset_id, result)
66+
session.add(taskset)
67+
session.flush()
68+
session.commit()
69+
return result
7170
finally:
7271
session.close()
7372

7473
def _restore_taskset(self, taskset_id):
7574
"""Get taskset metadata for a taskset by id."""
7675
session = self.ResultSession()
7776
try:
78-
qs = session.query(TaskSet)
79-
taskset = qs.filter(TaskSet.taskset_id == taskset_id).first()
77+
taskset = session.query(TaskSet).filter(
78+
TaskSet.taskset_id == taskset_id).first()
8079
if taskset:
8180
return taskset.to_dict()
8281
finally:

celery/db/models.py

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,14 @@ class Task(ResultModelBase):
2626
def __init__(self, task_id):
2727
self.task_id = task_id
2828

29-
def __str__(self):
30-
return "<Task(%s, %s, %s, %s)>" % (self.task_id,
31-
self.result,
32-
self.status,
33-
self.traceback)
34-
3529
def to_dict(self):
3630
return {"task_id": self.task_id,
3731
"status": self.status,
3832
"result": self.result,
39-
"date_done": self.date_done,
4033
"traceback": self.traceback}
4134

42-
def __unicode__(self):
43-
return u"<Task: %s successful: %s>" % (self.task_id, self.status)
44-
35+
def __repr__(self):
36+
return "<Task %s state: %s>" % (self.task_id, self.status)
4537

4638
class TaskSet(ResultModelBase):
4739
"""TaskSet result"""
@@ -55,16 +47,13 @@ class TaskSet(ResultModelBase):
5547
date_done = sa.Column(sa.DateTime, default=datetime.now,
5648
nullable=True)
5749

58-
def __init__(self, task_id):
59-
self.task_id = task_id
60-
61-
def __str__(self):
62-
return "<TaskSet(%s, %s)>" % (self.task_id, self.result)
50+
def __init__(self, taskset_id, result):
51+
self.taskset_id = taskset_id
52+
self.result = result
6353

6454
def to_dict(self):
6555
return {"taskset_id": self.taskset_id,
66-
"result": self.result,
67-
"date_done": self.date_done}
56+
"result": self.result}
6857

69-
def __unicode__(self):
58+
def __repr__(self):
7059
return u"<TaskSet: %s>" % (self.taskset_id, )

celery/events/state.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,19 @@
44
from carrot.utils import partition
55

66
from celery import states
7-
from celery.datastructures import LocalCache
7+
from celery.datastructures import AttributeDict, LocalCache
88
from celery.utils import kwdict
99

1010
HEARTBEAT_EXPIRE = 150 # 2 minutes, 30 seconds
1111

1212

13-
class Element(dict):
13+
class Element(AttributeDict):
1414
"""Base class for types."""
1515
visited = False
1616

1717
def __init__(self, **fields):
1818
dict.__init__(self, fields)
1919

20-
def __getattr__(self, key):
21-
try:
22-
return self[key]
23-
except KeyError:
24-
raise AttributeError("'%s' object has no attribute '%s'" % (
25-
self.__class__.__name__, key))
26-
27-
def __setattr__(self, key, value):
28-
self[key] = value
29-
3020

3121
class Worker(Element):
3222
"""Worker State."""
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import sys
2+
import types
3+
import unittest2 as unittest
4+
5+
from celery import states
6+
from celery.backends.cache import CacheBackend, get_best_memcache, DummyClient
7+
from celery.exceptions import ImproperlyConfigured
8+
from celery.utils import gen_unique_id
9+
10+
from celery.tests.utils import execute_context, mask_modules
11+
12+
13+
class SomeClass(object):
14+
15+
def __init__(self, data):
16+
self.data = data
17+
18+
19+
class test_CacheBackend(unittest.TestCase):
20+
21+
def test_mark_as_done(self):
22+
tb = CacheBackend(backend="memory://")
23+
24+
tid = gen_unique_id()
25+
26+
self.assertEqual(tb.get_status(tid), states.PENDING)
27+
self.assertIsNone(tb.get_result(tid))
28+
29+
tb.mark_as_done(tid, 42)
30+
self.assertEqual(tb.get_status(tid), states.SUCCESS)
31+
self.assertEqual(tb.get_result(tid), 42)
32+
33+
def test_is_pickled(self):
34+
tb = CacheBackend(backend="memory://")
35+
36+
tid2 = gen_unique_id()
37+
result = {"foo": "baz", "bar": SomeClass(12345)}
38+
tb.mark_as_done(tid2, result)
39+
# is serialized properly.
40+
rindb = tb.get_result(tid2)
41+
self.assertEqual(rindb.get("foo"), "baz")
42+
self.assertEqual(rindb.get("bar").data, 12345)
43+
44+
def test_mark_as_failure(self):
45+
tb = CacheBackend(backend="memory://")
46+
47+
tid3 = gen_unique_id()
48+
try:
49+
raise KeyError("foo")
50+
except KeyError, exception:
51+
pass
52+
tb.mark_as_failure(tid3, exception)
53+
self.assertEqual(tb.get_status(tid3), states.FAILURE)
54+
self.assertIsInstance(tb.get_result(tid3), KeyError)
55+
56+
def test_process_cleanup(self):
57+
tb = CacheBackend(backend="memory://")
58+
tb.process_cleanup()
59+
60+
def test_expires_as_int(self):
61+
tb = CacheBackend(backend="memory://", expires=10)
62+
self.assertEqual(tb.expires, 10)
63+
64+
def test_unknown_backend_raises_ImproperlyConfigured(self):
65+
self.assertRaises(ImproperlyConfigured,
66+
CacheBackend, backend="unknown://")
67+
68+
69+
70+
class test_get_best_memcache(unittest.TestCase):
71+
72+
def mock_memcache(self):
73+
memcache = types.ModuleType("memcache")
74+
memcache.Client = DummyClient
75+
memcache.Client.__module__ = memcache.__name__
76+
prev, sys.modules["memcache"] = sys.modules.get("memcache"), memcache
77+
yield
78+
if prev is not None:
79+
sys.modules["memcache"] = prev
80+
yield
81+
82+
def mock_pylibmc(self):
83+
pylibmc = types.ModuleType("pylibmc")
84+
pylibmc.Client = DummyClient
85+
pylibmc.Client.__module__ = pylibmc.__name__
86+
prev = sys.modules.get("pylibmc")
87+
sys.modules["pylibmc"] = pylibmc
88+
yield
89+
if prev is not None:
90+
sys.modules["pylibmc"] = prev
91+
yield
92+
93+
def test_pylibmc(self):
94+
pylibmc = self.mock_pylibmc()
95+
pylibmc.next()
96+
import __builtin__
97+
sys.modules.pop("celery.backends.cache", None)
98+
from celery.backends import cache
99+
self.assertEqual(cache.get_best_memcache().__module__, "pylibmc")
100+
pylibmc.next()
101+
102+
def test_memcache(self):
103+
104+
def with_no_pylibmc():
105+
sys.modules.pop("celery.backends.cache", None)
106+
from celery.backends import cache
107+
self.assertEqual(cache.get_best_memcache().__module__, "memcache")
108+
109+
context = mask_modules("pylibmc")
110+
context.__enter__()
111+
try:
112+
memcache = self.mock_memcache()
113+
memcache.next()
114+
with_no_pylibmc()
115+
memcache.next()
116+
finally:
117+
context.__exit__(None, None, None)
118+
119+
def test_no_implementations(self):
120+
121+
def with_no_memcache_libs():
122+
sys.modules.pop("celery.backends.cache", None)
123+
from celery.backends import cache
124+
self.assertRaises(ImproperlyConfigured, cache.get_best_memcache)
125+
126+
context = mask_modules("pylibmc", "memcache")
127+
context.__enter__()
128+
try:
129+
with_no_memcache_libs()
130+
finally:
131+
context.__exit__(None, None, None)

celery/tests/test_backends/test_database.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
import socket
33
import unittest2 as unittest
44

5+
from datetime import datetime
6+
57
from celery.exceptions import ImproperlyConfigured
68

9+
from celery import conf
710
from celery import states
11+
from celery.db.models import Task, TaskSet
812
from celery.utils import gen_unique_id
913
from celery.backends.database import DatabaseBackend
1014

@@ -19,6 +23,17 @@ def __init__(self, data):
1923

2024
class test_DatabaseBackend(unittest.TestCase):
2125

26+
def test_missing_dburi_raises_ImproperlyConfigured(self):
27+
prev, conf.RESULT_DBURI = conf.RESULT_DBURI, None
28+
try:
29+
self.assertRaises(ImproperlyConfigured, DatabaseBackend)
30+
finally:
31+
conf.RESULT_DBURI = prev
32+
33+
def test_missing_task_id_is_PENDING(self):
34+
tb = DatabaseBackend()
35+
self.assertEqual(tb.get_status("xxx-does-not-exist"), states.PENDING)
36+
2237
def test_mark_as_done(self):
2338
tb = DatabaseBackend()
2439

@@ -84,3 +99,40 @@ def test_mark_as_failure(self):
8499
def test_process_cleanup(self):
85100
tb = DatabaseBackend()
86101
tb.process_cleanup()
102+
103+
def test_save___restore_taskset(self):
104+
tb = DatabaseBackend()
105+
106+
tid = gen_unique_id()
107+
res = {u"something": "special"}
108+
self.assertEqual(tb.save_taskset(tid, res), res)
109+
110+
res2 = tb.restore_taskset(tid)
111+
self.assertEqual(res2, res)
112+
113+
self.assertIsNone(tb.restore_taskset("xxx-nonexisting-id"))
114+
115+
def test_cleanup(self):
116+
tb = DatabaseBackend()
117+
for i in range(10):
118+
tb.mark_as_done(gen_unique_id(), 42)
119+
tb.save_taskset(gen_unique_id(), {"foo": "bar"})
120+
s = tb.ResultSession()
121+
for t in s.query(Task).all():
122+
t.date_done = datetime.now() - tb.result_expires * 2
123+
for t in s.query(TaskSet).all():
124+
t.date_done = datetime.now() - tb.result_expires * 2
125+
s.commit()
126+
s.close()
127+
128+
tb.cleanup()
129+
s2 = tb.ResultSession()
130+
self.assertEqual(s2.query(Task).count(), 0)
131+
self.assertEqual(s2.query(TaskSet).count(), 0)
132+
133+
def test_Task__repr__(self):
134+
self.assertIn("foo", repr(Task("foo")))
135+
136+
def test_TaskSet__repr__(self):
137+
self.assertIn("foo", repr(TaskSet("foo", None)))
138+

0 commit comments

Comments
 (0)