mirror of https://github.com/celery/kombu.git
322 lines
8.6 KiB
Python
322 lines
8.6 KiB
Python
from __future__ import annotations
|
|
|
|
import pickle
|
|
from itertools import count
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
|
|
from kombu.utils import functional as utils
|
|
from kombu.utils.functional import (ChannelPromise, LRUCache, accepts_argument,
|
|
fxrange, fxrangemax, lazy, maybe_evaluate,
|
|
maybe_list, memoize, reprcall, reprkwargs,
|
|
retry_over_time)
|
|
|
|
|
|
class test_ChannelPromise:
|
|
|
|
def test_repr(self):
|
|
obj = Mock(name='cb')
|
|
assert 'promise' in repr(ChannelPromise(obj))
|
|
obj.assert_not_called()
|
|
|
|
|
|
class test_shufflecycle:
|
|
|
|
def test_shuffles(self):
|
|
prev_repeat, utils.repeat = utils.repeat, Mock()
|
|
try:
|
|
utils.repeat.return_value = list(range(10))
|
|
values = {'A', 'B', 'C'}
|
|
cycle = utils.shufflecycle(values)
|
|
seen = set()
|
|
for i in range(10):
|
|
next(cycle)
|
|
utils.repeat.assert_called_with(None)
|
|
assert seen.issubset(values)
|
|
with pytest.raises(StopIteration):
|
|
next(cycle)
|
|
next(cycle)
|
|
finally:
|
|
utils.repeat = prev_repeat
|
|
|
|
|
|
def double(x):
|
|
return x * 2
|
|
|
|
|
|
class test_LRUCache:
|
|
|
|
def test_expires(self):
|
|
limit = 100
|
|
x = LRUCache(limit=limit)
|
|
slots = list(range(limit * 2))
|
|
for i in slots:
|
|
x[i] = i
|
|
assert list(x.keys()) == list(slots[limit:])
|
|
assert x.items()
|
|
assert x.values()
|
|
|
|
def test_is_pickleable(self):
|
|
x = LRUCache(limit=10)
|
|
x.update(luke=1, leia=2)
|
|
y = pickle.loads(pickle.dumps(x))
|
|
assert y.limit == y.limit
|
|
assert y == x
|
|
|
|
def test_update_expires(self):
|
|
limit = 100
|
|
x = LRUCache(limit=limit)
|
|
slots = list(range(limit * 2))
|
|
for i in slots:
|
|
x.update({i: i})
|
|
|
|
assert list(x.keys()) == list(slots[limit:])
|
|
|
|
def test_least_recently_used(self):
|
|
x = LRUCache(3)
|
|
|
|
x[1], x[2], x[3] = 1, 2, 3
|
|
assert list(x.keys()), [1, 2 == 3]
|
|
|
|
x[4], x[5] = 4, 5
|
|
assert list(x.keys()), [3, 4 == 5]
|
|
|
|
# access 3, which makes it the last used key.
|
|
x[3]
|
|
x[6] = 6
|
|
assert list(x.keys()), [5, 3 == 6]
|
|
|
|
x[7] = 7
|
|
assert list(x.keys()), [3, 6 == 7]
|
|
|
|
def test_update_larger_than_cache_size(self):
|
|
x = LRUCache(2)
|
|
x.update({x: x for x in range(100)})
|
|
assert list(x.keys()), [98 == 99]
|
|
|
|
def test_items(self):
|
|
c = LRUCache()
|
|
c.update(a=1, b=2, c=3)
|
|
assert list(c.items())
|
|
|
|
def test_incr(self):
|
|
c = LRUCache()
|
|
c.update(a='1')
|
|
c.incr('a')
|
|
assert c['a'] == '2'
|
|
|
|
|
|
def test_memoize():
|
|
counter = count(1)
|
|
|
|
@memoize(maxsize=2)
|
|
def x(i):
|
|
return next(counter)
|
|
|
|
assert x(1) == 1
|
|
assert x(1) == 1
|
|
assert x(2) == 2
|
|
assert x(3) == 3
|
|
assert x(1) == 4
|
|
x.clear()
|
|
assert x(3) == 5
|
|
|
|
|
|
class test_lazy:
|
|
|
|
def test__str__(self):
|
|
assert (str(lazy(lambda: 'the quick brown fox')) ==
|
|
'the quick brown fox')
|
|
|
|
def test__repr__(self):
|
|
assert repr(lazy(lambda: 'fi fa fo')).strip('u') == "'fi fa fo'"
|
|
|
|
def test_evaluate(self):
|
|
assert lazy(lambda: 2 + 2)() == 4
|
|
assert lazy(lambda x: x * 4, 2) == 8
|
|
assert lazy(lambda x: x * 8, 2)() == 16
|
|
|
|
def test_cmp(self):
|
|
assert lazy(lambda: 10) == lazy(lambda: 10)
|
|
assert lazy(lambda: 10) != lazy(lambda: 20)
|
|
|
|
def test__reduce__(self):
|
|
x = lazy(double, 4)
|
|
y = pickle.loads(pickle.dumps(x))
|
|
assert x() == y()
|
|
|
|
def test__deepcopy__(self):
|
|
from copy import deepcopy
|
|
x = lazy(double, 4)
|
|
y = deepcopy(x)
|
|
assert x._fun == y._fun
|
|
assert x._args == y._args
|
|
assert x() == y()
|
|
|
|
|
|
@pytest.mark.parametrize('obj,expected', [
|
|
(lazy(lambda: 10), 10),
|
|
(20, 20),
|
|
])
|
|
def test_maybe_evaluate(obj, expected):
|
|
assert maybe_evaluate(obj) == expected
|
|
|
|
|
|
class test_retry_over_time:
|
|
|
|
class Predicate(Exception):
|
|
pass
|
|
|
|
def setup(self):
|
|
self.index = 0
|
|
|
|
def myfun(self):
|
|
if self.index < 9:
|
|
raise self.Predicate()
|
|
return 42
|
|
|
|
def errback(self, exc, intervals, retries):
|
|
interval = next(intervals)
|
|
sleepvals = (None, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 16.0)
|
|
self.index += 1
|
|
assert interval == sleepvals[self.index]
|
|
return interval
|
|
|
|
@pytest.mark.sleepdeprived_patched_module(utils)
|
|
def test_simple(self, sleepdeprived):
|
|
prev_count, utils.count = utils.count, Mock()
|
|
try:
|
|
utils.count.return_value = list(range(1))
|
|
x = retry_over_time(self.myfun, self.Predicate,
|
|
errback=None, interval_max=14)
|
|
assert x is None
|
|
utils.count.return_value = list(range(10))
|
|
cb = Mock()
|
|
x = retry_over_time(self.myfun, self.Predicate,
|
|
errback=self.errback, callback=cb,
|
|
interval_max=14)
|
|
assert x == 42
|
|
assert self.index == 9
|
|
cb.assert_called_with()
|
|
finally:
|
|
utils.count = prev_count
|
|
|
|
def test_retry_timeout(self):
|
|
|
|
with pytest.raises(self.Predicate):
|
|
retry_over_time(
|
|
self.myfun, self.Predicate,
|
|
errback=self.errback, interval_max=14, timeout=1
|
|
)
|
|
assert self.index == 1
|
|
|
|
# no errback
|
|
with pytest.raises(self.Predicate):
|
|
retry_over_time(
|
|
self.myfun, self.Predicate,
|
|
errback=None, timeout=1,
|
|
)
|
|
|
|
@pytest.mark.sleepdeprived_patched_module(utils)
|
|
def test_retry_zero(self, sleepdeprived):
|
|
with pytest.raises(self.Predicate):
|
|
retry_over_time(
|
|
self.myfun, self.Predicate,
|
|
max_retries=0, errback=self.errback, interval_max=14,
|
|
)
|
|
assert self.index == 0
|
|
# no errback
|
|
with pytest.raises(self.Predicate):
|
|
retry_over_time(
|
|
self.myfun, self.Predicate,
|
|
max_retries=0, errback=None, interval_max=14,
|
|
)
|
|
|
|
@pytest.mark.sleepdeprived_patched_module(utils)
|
|
def test_retry_once(self, sleepdeprived):
|
|
with pytest.raises(self.Predicate):
|
|
retry_over_time(
|
|
self.myfun, self.Predicate,
|
|
max_retries=1, errback=self.errback, interval_max=14,
|
|
)
|
|
assert self.index == 1
|
|
# no errback
|
|
with pytest.raises(self.Predicate):
|
|
retry_over_time(
|
|
self.myfun, self.Predicate,
|
|
max_retries=1, errback=None, interval_max=14,
|
|
)
|
|
|
|
@pytest.mark.sleepdeprived_patched_module(utils)
|
|
def test_retry_always(self, sleepdeprived):
|
|
Predicate = self.Predicate
|
|
|
|
class Fun:
|
|
|
|
def __init__(self):
|
|
self.calls = 0
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
try:
|
|
if self.calls >= 10:
|
|
return 42
|
|
raise Predicate()
|
|
finally:
|
|
self.calls += 1
|
|
fun = Fun()
|
|
|
|
assert retry_over_time(
|
|
fun, self.Predicate,
|
|
max_retries=None, errback=None, interval_max=14) == 42
|
|
assert fun.calls == 11
|
|
|
|
|
|
@pytest.mark.parametrize('obj,expected', [
|
|
(None, None),
|
|
(1, [1]),
|
|
([1, 2, 3], [1, 2, 3]),
|
|
])
|
|
def test_maybe_list(obj, expected):
|
|
assert maybe_list(obj) == expected
|
|
|
|
|
|
def test_fxrange__no_repeatlast():
|
|
assert list(fxrange(1.0, 3.0, 1.0)) == [1.0, 2.0, 3.0]
|
|
|
|
|
|
@pytest.mark.parametrize('args,expected', [
|
|
((1.0, 3.0, 1.0, 30.0),
|
|
[1.0, 2.0, 3.0, 3.0, 3.0, 3.0,
|
|
3.0, 3.0, 3.0, 3.0, 3.0]),
|
|
((1.0, None, 1.0, 30.0),
|
|
[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]),
|
|
])
|
|
def test_fxrangemax(args, expected):
|
|
assert list(fxrangemax(*args)) == expected
|
|
|
|
|
|
def test_reprkwargs():
|
|
assert reprkwargs({'foo': 'bar', 1: 2, 'k': 'v'})
|
|
|
|
|
|
def test_reprcall():
|
|
assert reprcall('add', (2, 2), {'copy': True})
|
|
|
|
|
|
class test_accepts_arg:
|
|
def function(self, foo, bar, baz="baz"):
|
|
pass
|
|
|
|
def test_valid_argument(self):
|
|
assert accepts_argument(self.function, 'self')
|
|
assert accepts_argument(self.function, 'foo')
|
|
assert accepts_argument(self.function, 'baz')
|
|
|
|
def test_invalid_argument(self):
|
|
assert not accepts_argument(self.function, 'random_argument')
|
|
|
|
def test_raise_exception(self):
|
|
with pytest.raises(Exception):
|
|
accepts_argument(None, 'foo')
|