test_tenacity.py 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310
  1. # Copyright 2016 Julien Danjou
  2. # Copyright 2016 Joshua Harlow
  3. # Copyright 2013 Ray Holder
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. import logging
  17. import time
  18. import unittest
  19. import warnings
  20. from contextlib import contextmanager
  21. from copy import copy
  22. import pytest
  23. import six.moves
  24. import tenacity
  25. from tenacity import RetryError, Retrying, retry
  26. from tenacity.compat import make_retry_state
  27. class TestBase(unittest.TestCase):
  28. def test_repr(self):
  29. repr(tenacity.BaseRetrying())
  30. class TestStopConditions(unittest.TestCase):
  31. def test_never_stop(self):
  32. r = Retrying()
  33. self.assertFalse(r.stop(make_retry_state(3, 6546)))
  34. def test_stop_any(self):
  35. stop = tenacity.stop_any(
  36. tenacity.stop_after_delay(1),
  37. tenacity.stop_after_attempt(4))
  38. def s(*args):
  39. return stop(make_retry_state(*args))
  40. self.assertFalse(s(1, 0.1))
  41. self.assertFalse(s(2, 0.2))
  42. self.assertFalse(s(2, 0.8))
  43. self.assertTrue(s(4, 0.8))
  44. self.assertTrue(s(3, 1.8))
  45. self.assertTrue(s(4, 1.8))
  46. def test_stop_all(self):
  47. stop = tenacity.stop_all(
  48. tenacity.stop_after_delay(1),
  49. tenacity.stop_after_attempt(4))
  50. def s(*args):
  51. return stop(make_retry_state(*args))
  52. self.assertFalse(s(1, 0.1))
  53. self.assertFalse(s(2, 0.2))
  54. self.assertFalse(s(2, 0.8))
  55. self.assertFalse(s(4, 0.8))
  56. self.assertFalse(s(3, 1.8))
  57. self.assertTrue(s(4, 1.8))
  58. def test_stop_or(self):
  59. stop = tenacity.stop_after_delay(1) | tenacity.stop_after_attempt(4)
  60. def s(*args):
  61. return stop(make_retry_state(*args))
  62. self.assertFalse(s(1, 0.1))
  63. self.assertFalse(s(2, 0.2))
  64. self.assertFalse(s(2, 0.8))
  65. self.assertTrue(s(4, 0.8))
  66. self.assertTrue(s(3, 1.8))
  67. self.assertTrue(s(4, 1.8))
  68. def test_stop_and(self):
  69. stop = tenacity.stop_after_delay(1) & tenacity.stop_after_attempt(4)
  70. def s(*args):
  71. return stop(make_retry_state(*args))
  72. self.assertFalse(s(1, 0.1))
  73. self.assertFalse(s(2, 0.2))
  74. self.assertFalse(s(2, 0.8))
  75. self.assertFalse(s(4, 0.8))
  76. self.assertFalse(s(3, 1.8))
  77. self.assertTrue(s(4, 1.8))
  78. def test_stop_after_attempt(self):
  79. r = Retrying(stop=tenacity.stop_after_attempt(3))
  80. self.assertFalse(r.stop(make_retry_state(2, 6546)))
  81. self.assertTrue(r.stop(make_retry_state(3, 6546)))
  82. self.assertTrue(r.stop(make_retry_state(4, 6546)))
  83. def test_stop_after_delay(self):
  84. r = Retrying(stop=tenacity.stop_after_delay(1))
  85. self.assertFalse(r.stop(make_retry_state(2, 0.999)))
  86. self.assertTrue(r.stop(make_retry_state(2, 1)))
  87. self.assertTrue(r.stop(make_retry_state(2, 1.001)))
  88. def test_legacy_explicit_stop_type(self):
  89. Retrying(stop="stop_after_attempt")
  90. def test_stop_backward_compat(self):
  91. r = Retrying(stop=lambda attempt, delay: attempt == delay)
  92. with reports_deprecation_warning():
  93. self.assertFalse(r.stop(make_retry_state(1, 3)))
  94. with reports_deprecation_warning():
  95. self.assertFalse(r.stop(make_retry_state(100, 99)))
  96. with reports_deprecation_warning():
  97. self.assertTrue(r.stop(make_retry_state(101, 101)))
  98. def test_retry_child_class_with_override_backward_compat(self):
  99. class MyStop(tenacity.stop_after_attempt):
  100. def __init__(self):
  101. super(MyStop, self).__init__(1)
  102. def __call__(self, attempt_number, seconds_since_start):
  103. return super(MyStop, self).__call__(
  104. attempt_number, seconds_since_start)
  105. retrying = Retrying(wait=tenacity.wait_fixed(0.01),
  106. stop=MyStop())
  107. def failing():
  108. raise NotImplementedError()
  109. with pytest.raises(RetryError):
  110. retrying.call(failing)
  111. def test_stop_func_with_retry_state(self):
  112. def stop_func(retry_state):
  113. rs = retry_state
  114. return rs.attempt_number == rs.seconds_since_start
  115. r = Retrying(stop=stop_func)
  116. self.assertFalse(r.stop(make_retry_state(1, 3)))
  117. self.assertFalse(r.stop(make_retry_state(100, 99)))
  118. self.assertTrue(r.stop(make_retry_state(101, 101)))
  119. class TestWaitConditions(unittest.TestCase):
  120. def test_no_sleep(self):
  121. r = Retrying()
  122. self.assertEqual(0, r.wait(18, 9879))
  123. def test_fixed_sleep(self):
  124. r = Retrying(wait=tenacity.wait_fixed(1))
  125. self.assertEqual(1, r.wait(12, 6546))
  126. def test_incrementing_sleep(self):
  127. r = Retrying(wait=tenacity.wait_incrementing(
  128. start=500, increment=100))
  129. self.assertEqual(500, r.wait(1, 6546))
  130. self.assertEqual(600, r.wait(2, 6546))
  131. self.assertEqual(700, r.wait(3, 6546))
  132. def test_random_sleep(self):
  133. r = Retrying(wait=tenacity.wait_random(min=1, max=20))
  134. times = set()
  135. for x in six.moves.range(1000):
  136. times.add(r.wait(1, 6546))
  137. # this is kind of non-deterministic...
  138. self.assertTrue(len(times) > 1)
  139. for t in times:
  140. self.assertTrue(t >= 1)
  141. self.assertTrue(t < 20)
  142. def test_random_sleep_without_min(self):
  143. r = Retrying(wait=tenacity.wait_random(max=2))
  144. times = set()
  145. times.add(r.wait(1, 6546))
  146. times.add(r.wait(1, 6546))
  147. times.add(r.wait(1, 6546))
  148. times.add(r.wait(1, 6546))
  149. # this is kind of non-deterministic...
  150. self.assertTrue(len(times) > 1)
  151. for t in times:
  152. self.assertTrue(t >= 0)
  153. self.assertTrue(t <= 2)
  154. def test_exponential(self):
  155. r = Retrying(wait=tenacity.wait_exponential(max=100))
  156. self.assertEqual(r.wait(1, 0), 2)
  157. self.assertEqual(r.wait(2, 0), 4)
  158. self.assertEqual(r.wait(3, 0), 8)
  159. self.assertEqual(r.wait(4, 0), 16)
  160. self.assertEqual(r.wait(5, 0), 32)
  161. self.assertEqual(r.wait(6, 0), 64)
  162. def test_exponential_with_max_wait(self):
  163. r = Retrying(wait=tenacity.wait_exponential(max=40))
  164. self.assertEqual(r.wait(1, 0), 2)
  165. self.assertEqual(r.wait(2, 0), 4)
  166. self.assertEqual(r.wait(3, 0), 8)
  167. self.assertEqual(r.wait(4, 0), 16)
  168. self.assertEqual(r.wait(5, 0), 32)
  169. self.assertEqual(r.wait(6, 0), 40)
  170. self.assertEqual(r.wait(7, 0), 40)
  171. self.assertEqual(r.wait(50, 0), 40)
  172. def test_exponential_with_min_wait(self):
  173. r = Retrying(wait=tenacity.wait_exponential(min=20))
  174. self.assertEqual(r.wait(1, 0), 20)
  175. self.assertEqual(r.wait(2, 0), 20)
  176. self.assertEqual(r.wait(3, 0), 20)
  177. self.assertEqual(r.wait(4, 0), 20)
  178. self.assertEqual(r.wait(5, 0), 32)
  179. self.assertEqual(r.wait(6, 0), 64)
  180. self.assertEqual(r.wait(7, 0), 128)
  181. self.assertEqual(r.wait(20, 0), 1048576)
  182. def test_exponential_with_max_wait_and_multiplier(self):
  183. r = Retrying(wait=tenacity.wait_exponential(
  184. max=50, multiplier=1))
  185. self.assertEqual(r.wait(1, 0), 2)
  186. self.assertEqual(r.wait(2, 0), 4)
  187. self.assertEqual(r.wait(3, 0), 8)
  188. self.assertEqual(r.wait(4, 0), 16)
  189. self.assertEqual(r.wait(5, 0), 32)
  190. self.assertEqual(r.wait(6, 0), 50)
  191. self.assertEqual(r.wait(7, 0), 50)
  192. self.assertEqual(r.wait(50, 0), 50)
  193. def test_exponential_with_min_wait_and_multiplier(self):
  194. r = Retrying(wait=tenacity.wait_exponential(
  195. min=20, multiplier=2))
  196. self.assertEqual(r.wait(1, 0), 20)
  197. self.assertEqual(r.wait(2, 0), 20)
  198. self.assertEqual(r.wait(3, 0), 20)
  199. self.assertEqual(r.wait(4, 0), 32)
  200. self.assertEqual(r.wait(5, 0), 64)
  201. self.assertEqual(r.wait(6, 0), 128)
  202. self.assertEqual(r.wait(7, 0), 256)
  203. self.assertEqual(r.wait(20, 0), 2097152)
  204. def test_exponential_with_min_wait_and_max_wait(self):
  205. r = Retrying(wait=tenacity.wait_exponential(min=10, max=100))
  206. self.assertEqual(r.wait(1, 0), 10)
  207. self.assertEqual(r.wait(2, 0), 10)
  208. self.assertEqual(r.wait(3, 0), 10)
  209. self.assertEqual(r.wait(4, 0), 16)
  210. self.assertEqual(r.wait(5, 0), 32)
  211. self.assertEqual(r.wait(6, 0), 64)
  212. self.assertEqual(r.wait(7, 0), 100)
  213. self.assertEqual(r.wait(20, 0), 100)
  214. def test_legacy_explicit_wait_type(self):
  215. Retrying(wait="exponential_sleep")
  216. def test_wait_backward_compat_with_result(self):
  217. captures = []
  218. def wait_capture(attempt, delay, last_result=None):
  219. captures.append(last_result)
  220. return 1
  221. def dying():
  222. raise Exception("Broken")
  223. r_attempts = 10
  224. r = Retrying(wait=wait_capture, sleep=lambda secs: None,
  225. stop=tenacity.stop_after_attempt(r_attempts),
  226. reraise=True)
  227. with reports_deprecation_warning():
  228. self.assertRaises(Exception, r.call, dying)
  229. self.assertEqual(r_attempts - 1, len(captures))
  230. self.assertTrue(all([r.failed for r in captures]))
  231. def test_wait_func(self):
  232. def wait_func(retry_state):
  233. return retry_state.attempt_number * retry_state.seconds_since_start
  234. r = Retrying(wait=wait_func)
  235. self.assertEqual(r.wait(make_retry_state(1, 5)), 5)
  236. self.assertEqual(r.wait(make_retry_state(2, 11)), 22)
  237. self.assertEqual(r.wait(make_retry_state(10, 100)), 1000)
  238. def test_wait_combine(self):
  239. r = Retrying(wait=tenacity.wait_combine(tenacity.wait_random(0, 3),
  240. tenacity.wait_fixed(5)))
  241. # Test it a few time since it's random
  242. for i in six.moves.range(1000):
  243. w = r.wait(1, 5)
  244. self.assertLess(w, 8)
  245. self.assertGreaterEqual(w, 5)
  246. def test_wait_double_sum(self):
  247. r = Retrying(wait=tenacity.wait_random(0, 3) + tenacity.wait_fixed(5))
  248. # Test it a few time since it's random
  249. for i in six.moves.range(1000):
  250. w = r.wait(1, 5)
  251. self.assertLess(w, 8)
  252. self.assertGreaterEqual(w, 5)
  253. def test_wait_triple_sum(self):
  254. r = Retrying(wait=tenacity.wait_fixed(1) + tenacity.wait_random(0, 3) +
  255. tenacity.wait_fixed(5))
  256. # Test it a few time since it's random
  257. for i in six.moves.range(1000):
  258. w = r.wait(1, 5)
  259. self.assertLess(w, 9)
  260. self.assertGreaterEqual(w, 6)
  261. def test_wait_arbitrary_sum(self):
  262. r = Retrying(wait=sum([tenacity.wait_fixed(1),
  263. tenacity.wait_random(0, 3),
  264. tenacity.wait_fixed(5),
  265. tenacity.wait_none()]))
  266. # Test it a few time since it's random
  267. for i in six.moves.range(1000):
  268. w = r.wait(1, 5)
  269. self.assertLess(w, 9)
  270. self.assertGreaterEqual(w, 6)
  271. def _assert_range(self, wait, min_, max_):
  272. self.assertLess(wait, max_)
  273. self.assertGreaterEqual(wait, min_)
  274. def _assert_inclusive_range(self, wait, low, high):
  275. self.assertLessEqual(wait, high)
  276. self.assertGreaterEqual(wait, low)
  277. def test_wait_chain(self):
  278. r = Retrying(wait=tenacity.wait_chain(
  279. *[tenacity.wait_fixed(1) for i in six.moves.range(2)] +
  280. [tenacity.wait_fixed(4) for i in six.moves.range(2)] +
  281. [tenacity.wait_fixed(8) for i in six.moves.range(1)]))
  282. for i in six.moves.range(10):
  283. w = r.wait(i + 1, 1)
  284. if i < 2:
  285. self._assert_range(w, 1, 2)
  286. elif i < 4:
  287. self._assert_range(w, 4, 5)
  288. else:
  289. self._assert_range(w, 8, 9)
  290. def test_wait_chain_multiple_invocations(self):
  291. sleep_intervals = []
  292. r = Retrying(
  293. sleep=sleep_intervals.append,
  294. wait=tenacity.wait_chain(*[
  295. tenacity.wait_fixed(i + 1) for i in six.moves.range(3)
  296. ]),
  297. stop=tenacity.stop_after_attempt(5),
  298. retry=tenacity.retry_if_result(lambda x: x == 1),
  299. )
  300. @r.wraps
  301. def always_return_1():
  302. return 1
  303. self.assertRaises(tenacity.RetryError, always_return_1)
  304. self.assertEqual(sleep_intervals, [1.0, 2.0, 3.0, 3.0])
  305. sleep_intervals[:] = []
  306. # Clear and restart retrying.
  307. self.assertRaises(tenacity.RetryError, always_return_1)
  308. self.assertEqual(sleep_intervals, [1.0, 2.0, 3.0, 3.0])
  309. sleep_intervals[:] = []
  310. def test_wait_random_exponential(self):
  311. fn = tenacity.wait_random_exponential(0.5, 60.0)
  312. for _ in six.moves.range(1000):
  313. self._assert_inclusive_range(fn(0, 0), 0, 0.5)
  314. self._assert_inclusive_range(fn(1, 0), 0, 1.0)
  315. self._assert_inclusive_range(fn(2, 0), 0, 2.0)
  316. self._assert_inclusive_range(fn(3, 0), 0, 4.0)
  317. self._assert_inclusive_range(fn(4, 0), 0, 8.0)
  318. self._assert_inclusive_range(fn(5, 0), 0, 16.0)
  319. self._assert_inclusive_range(fn(6, 0), 0, 32.0)
  320. self._assert_inclusive_range(fn(7, 0), 0, 60.0)
  321. self._assert_inclusive_range(fn(8, 0), 0, 60.0)
  322. self._assert_inclusive_range(fn(9, 0), 0, 60.0)
  323. fn = tenacity.wait_random_exponential(10, 5)
  324. for _ in six.moves.range(1000):
  325. self._assert_inclusive_range(fn(0, 0), 0.00, 5.00)
  326. # Default arguments exist
  327. fn = tenacity.wait_random_exponential()
  328. fn(0, 0)
  329. def test_wait_random_exponential_statistically(self):
  330. fn = tenacity.wait_random_exponential(0.5, 60.0)
  331. attempt = []
  332. for i in six.moves.range(10):
  333. attempt.append(
  334. [fn(i, 0) for _ in six.moves.range(4000)]
  335. )
  336. def mean(lst):
  337. return float(sum(lst)) / float(len(lst))
  338. self._assert_inclusive_range(mean(attempt[0]), 0.20, 0.30)
  339. self._assert_inclusive_range(mean(attempt[1]), 0.35, 0.65)
  340. self._assert_inclusive_range(mean(attempt[2]), 0.75, 1.25)
  341. self._assert_inclusive_range(mean(attempt[3]), 1.75, 3.25)
  342. self._assert_inclusive_range(mean(attempt[4]), 3.50, 5.50)
  343. self._assert_inclusive_range(mean(attempt[5]), 7.00, 9.00)
  344. self._assert_inclusive_range(mean(attempt[6]), 14.00, 18.00)
  345. self._assert_inclusive_range(mean(attempt[7]), 28.00, 34.00)
  346. self._assert_inclusive_range(mean(attempt[8]), 28.00, 34.00)
  347. self._assert_inclusive_range(mean(attempt[9]), 28.00, 34.00)
  348. def test_wait_backward_compat(self):
  349. """Ensure Retrying object accepts both old and newstyle wait funcs."""
  350. def wait1(previous_attempt_number, delay_since_first_attempt):
  351. wait1.calls.append((
  352. previous_attempt_number, delay_since_first_attempt))
  353. return 0
  354. wait1.calls = []
  355. def wait2(previous_attempt_number, delay_since_first_attempt,
  356. last_result):
  357. wait2.calls.append((
  358. previous_attempt_number, delay_since_first_attempt,
  359. last_result))
  360. return 0
  361. wait2.calls = []
  362. def dying():
  363. raise Exception("Broken")
  364. retrying1 = Retrying(wait=wait1, stop=tenacity.stop_after_attempt(4))
  365. with reports_deprecation_warning():
  366. self.assertRaises(Exception, lambda: retrying1.call(dying))
  367. self.assertEqual([t[0] for t in wait1.calls], [1, 2, 3])
  368. # This assumes that 3 iterations complete within 1 second.
  369. self.assertTrue(all(t[1] < 1 for t in wait1.calls))
  370. retrying2 = Retrying(wait=wait2, stop=tenacity.stop_after_attempt(4))
  371. with reports_deprecation_warning():
  372. self.assertRaises(Exception, lambda: retrying2.call(dying))
  373. self.assertEqual([t[0] for t in wait2.calls], [1, 2, 3])
  374. # This assumes that 3 iterations complete within 1 second.
  375. self.assertTrue(all(t[1] < 1 for t in wait2.calls))
  376. self.assertEqual([str(t[2].exception()) for t in wait2.calls],
  377. ['Broken'] * 3)
  378. def test_wait_class_backward_compatibility(self):
  379. """Ensure builtin objects accept both old and new parameters."""
  380. waitobj = tenacity.wait_fixed(5)
  381. self.assertEqual(waitobj(1, 0.1), 5)
  382. self.assertEqual(
  383. waitobj(1, 0.1, tenacity.Future.construct(1, 1, False)), 5)
  384. retry_state = make_retry_state(123, 456)
  385. self.assertEqual(retry_state.attempt_number, 123)
  386. self.assertEqual(retry_state.seconds_since_start, 456)
  387. self.assertEqual(waitobj(retry_state=retry_state), 5)
  388. def test_wait_retry_state_attributes(self):
  389. class ExtractCallState(Exception):
  390. pass
  391. # retry_state is mutable, so return it as an exception to extract the
  392. # exact values it has when wait is called and bypass any other logic.
  393. def waitfunc(retry_state):
  394. raise ExtractCallState(retry_state)
  395. retrying = Retrying(
  396. wait=waitfunc,
  397. retry=(tenacity.retry_if_exception_type() |
  398. tenacity.retry_if_result(lambda result: result == 123)))
  399. def returnval():
  400. return 123
  401. try:
  402. retrying.call(returnval)
  403. except ExtractCallState as err:
  404. retry_state = err.args[0]
  405. self.assertIs(retry_state.fn, returnval)
  406. self.assertEqual(retry_state.args, ())
  407. self.assertEqual(retry_state.kwargs, {})
  408. self.assertEqual(retry_state.outcome.result(), 123)
  409. self.assertEqual(retry_state.attempt_number, 1)
  410. self.assertGreater(retry_state.outcome_timestamp,
  411. retry_state.start_time)
  412. def dying():
  413. raise Exception("Broken")
  414. try:
  415. retrying.call(dying)
  416. except ExtractCallState as err:
  417. retry_state = err.args[0]
  418. self.assertIs(retry_state.fn, dying)
  419. self.assertEqual(retry_state.args, ())
  420. self.assertEqual(retry_state.kwargs, {})
  421. self.assertEqual(str(retry_state.outcome.exception()), 'Broken')
  422. self.assertEqual(retry_state.attempt_number, 1)
  423. self.assertGreater(retry_state.outcome_timestamp,
  424. retry_state.start_time)
  425. class TestRetryConditions(unittest.TestCase):
  426. def test_retry_if_result(self):
  427. retry = (tenacity.retry_if_result(lambda x: x == 1))
  428. def r(fut):
  429. retry_state = make_retry_state(1, 1.0, last_result=fut)
  430. return retry(retry_state)
  431. self.assertTrue(r(tenacity.Future.construct(1, 1, False)))
  432. self.assertFalse(r(tenacity.Future.construct(1, 2, False)))
  433. def test_retry_if_not_result(self):
  434. retry = (tenacity.retry_if_not_result(lambda x: x == 1))
  435. def r(fut):
  436. retry_state = make_retry_state(1, 1.0, last_result=fut)
  437. return retry(retry_state)
  438. self.assertTrue(r(tenacity.Future.construct(1, 2, False)))
  439. self.assertFalse(r(tenacity.Future.construct(1, 1, False)))
  440. def test_retry_any(self):
  441. retry = tenacity.retry_any(
  442. tenacity.retry_if_result(lambda x: x == 1),
  443. tenacity.retry_if_result(lambda x: x == 2))
  444. def r(fut):
  445. retry_state = make_retry_state(1, 1.0, last_result=fut)
  446. return retry(retry_state)
  447. self.assertTrue(r(tenacity.Future.construct(1, 1, False)))
  448. self.assertTrue(r(tenacity.Future.construct(1, 2, False)))
  449. self.assertFalse(r(tenacity.Future.construct(1, 3, False)))
  450. self.assertFalse(r(tenacity.Future.construct(1, 1, True)))
  451. def test_retry_all(self):
  452. retry = tenacity.retry_all(
  453. tenacity.retry_if_result(lambda x: x == 1),
  454. tenacity.retry_if_result(lambda x: isinstance(x, int)))
  455. def r(fut):
  456. retry_state = make_retry_state(1, 1.0, last_result=fut)
  457. return retry(retry_state)
  458. self.assertTrue(r(tenacity.Future.construct(1, 1, False)))
  459. self.assertFalse(r(tenacity.Future.construct(1, 2, False)))
  460. self.assertFalse(r(tenacity.Future.construct(1, 3, False)))
  461. self.assertFalse(r(tenacity.Future.construct(1, 1, True)))
  462. def test_retry_and(self):
  463. retry = (tenacity.retry_if_result(lambda x: x == 1) &
  464. tenacity.retry_if_result(lambda x: isinstance(x, int)))
  465. def r(fut):
  466. retry_state = make_retry_state(1, 1.0, last_result=fut)
  467. return retry(retry_state)
  468. self.assertTrue(r(tenacity.Future.construct(1, 1, False)))
  469. self.assertFalse(r(tenacity.Future.construct(1, 2, False)))
  470. self.assertFalse(r(tenacity.Future.construct(1, 3, False)))
  471. self.assertFalse(r(tenacity.Future.construct(1, 1, True)))
  472. def test_retry_or(self):
  473. retry = (tenacity.retry_if_result(lambda x: x == "foo") |
  474. tenacity.retry_if_result(lambda x: isinstance(x, int)))
  475. def r(fut):
  476. retry_state = make_retry_state(1, 1.0, last_result=fut)
  477. return retry(retry_state)
  478. self.assertTrue(r(tenacity.Future.construct(1, "foo", False)))
  479. self.assertFalse(r(tenacity.Future.construct(1, "foobar", False)))
  480. self.assertFalse(r(tenacity.Future.construct(1, 2.2, False)))
  481. self.assertFalse(r(tenacity.Future.construct(1, 42, True)))
  482. def _raise_try_again(self):
  483. self._attempts += 1
  484. if self._attempts < 3:
  485. raise tenacity.TryAgain
  486. def test_retry_try_again(self):
  487. self._attempts = 0
  488. Retrying(stop=tenacity.stop_after_attempt(5),
  489. retry=tenacity.retry_never).call(self._raise_try_again)
  490. self.assertEqual(3, self._attempts)
  491. def test_retry_try_again_forever(self):
  492. def _r():
  493. raise tenacity.TryAgain
  494. r = Retrying(stop=tenacity.stop_after_attempt(5),
  495. retry=tenacity.retry_never)
  496. self.assertRaises(tenacity.RetryError,
  497. r.call,
  498. _r)
  499. self.assertEqual(5, r.statistics['attempt_number'])
  500. def test_retry_try_again_forever_reraise(self):
  501. def _r():
  502. raise tenacity.TryAgain
  503. r = Retrying(stop=tenacity.stop_after_attempt(5),
  504. retry=tenacity.retry_never,
  505. reraise=True)
  506. self.assertRaises(tenacity.TryAgain,
  507. r,
  508. _r)
  509. self.assertEqual(5, r.statistics['attempt_number'])
  510. def test_retry_if_exception_message_negative_no_inputs(self):
  511. with self.assertRaises(TypeError):
  512. tenacity.retry_if_exception_message()
  513. def test_retry_if_exception_message_negative_too_many_inputs(self):
  514. with self.assertRaises(TypeError):
  515. tenacity.retry_if_exception_message(
  516. message="negative", match="negative")
  517. class NoneReturnUntilAfterCount(object):
  518. """Holds counter state for invoking a method several times in a row."""
  519. def __init__(self, count):
  520. self.counter = 0
  521. self.count = count
  522. def go(self):
  523. """Return None until after count threshold has been crossed.
  524. Then return True.
  525. """
  526. if self.counter < self.count:
  527. self.counter += 1
  528. return None
  529. return True
  530. class NoIOErrorAfterCount(object):
  531. """Holds counter state for invoking a method several times in a row."""
  532. def __init__(self, count):
  533. self.counter = 0
  534. self.count = count
  535. def go(self):
  536. """Raise an IOError until after count threshold has been crossed.
  537. Then return True.
  538. """
  539. if self.counter < self.count:
  540. self.counter += 1
  541. raise IOError("Hi there, I'm an IOError")
  542. return True
  543. class NoNameErrorAfterCount(object):
  544. """Holds counter state for invoking a method several times in a row."""
  545. def __init__(self, count):
  546. self.counter = 0
  547. self.count = count
  548. def go(self):
  549. """Raise a NameError until after count threshold has been crossed.
  550. Then return True.
  551. """
  552. if self.counter < self.count:
  553. self.counter += 1
  554. raise NameError("Hi there, I'm a NameError")
  555. return True
  556. class NameErrorUntilCount(object):
  557. """Holds counter state for invoking a method several times in a row."""
  558. derived_message = "Hi there, I'm a NameError"
  559. def __init__(self, count):
  560. self.counter = 0
  561. self.count = count
  562. def go(self):
  563. """Return True until after count threshold has been crossed.
  564. Then raise a NameError.
  565. """
  566. if self.counter < self.count:
  567. self.counter += 1
  568. return True
  569. raise NameError(self.derived_message)
  570. class IOErrorUntilCount(object):
  571. """Holds counter state for invoking a method several times in a row."""
  572. def __init__(self, count):
  573. self.counter = 0
  574. self.count = count
  575. def go(self):
  576. """Return True until after count threshold has been crossed.
  577. Then raise an IOError.
  578. """
  579. if self.counter < self.count:
  580. self.counter += 1
  581. return True
  582. raise IOError("Hi there, I'm an IOError")
  583. class CustomError(Exception):
  584. """This is a custom exception class.
  585. Note that For Python 2.x, we don't strictly need to extend BaseException,
  586. however, Python 3.x will complain. While this test suite won't run
  587. correctly under Python 3.x without extending from the Python exception
  588. hierarchy, the actual module code is backwards compatible Python 2.x and
  589. will allow for cases where exception classes don't extend from the
  590. hierarchy.
  591. """
  592. def __init__(self, value):
  593. self.value = value
  594. def __str__(self):
  595. return self.value
  596. class NoCustomErrorAfterCount(object):
  597. """Holds counter state for invoking a method several times in a row."""
  598. derived_message = "This is a Custom exception class"
  599. def __init__(self, count):
  600. self.counter = 0
  601. self.count = count
  602. def go(self):
  603. """Raise a CustomError until after count threshold has been crossed.
  604. Then return True.
  605. """
  606. if self.counter < self.count:
  607. self.counter += 1
  608. raise CustomError(self.derived_message)
  609. return True
  610. class CapturingHandler(logging.Handler):
  611. """Captures log records for inspection."""
  612. def __init__(self, *args, **kwargs):
  613. super(CapturingHandler, self).__init__(*args, **kwargs)
  614. self.records = []
  615. def emit(self, record):
  616. self.records.append(record)
  617. def current_time_ms():
  618. return int(round(time.time() * 1000))
  619. @retry(wait=tenacity.wait_fixed(0.05),
  620. retry=tenacity.retry_if_result(lambda result: result is None))
  621. def _retryable_test_with_wait(thing):
  622. return thing.go()
  623. @retry(stop=tenacity.stop_after_attempt(3),
  624. retry=tenacity.retry_if_result(lambda result: result is None))
  625. def _retryable_test_with_stop(thing):
  626. return thing.go()
  627. @retry(retry=tenacity.retry_if_exception_type(IOError))
  628. def _retryable_test_with_exception_type_io(thing):
  629. return thing.go()
  630. @retry(
  631. stop=tenacity.stop_after_attempt(3),
  632. retry=tenacity.retry_if_exception_type(IOError))
  633. def _retryable_test_with_exception_type_io_attempt_limit(thing):
  634. return thing.go()
  635. @retry(retry=tenacity.retry_unless_exception_type(NameError))
  636. def _retryable_test_with_unless_exception_type_name(thing):
  637. return thing.go()
  638. @retry(
  639. stop=tenacity.stop_after_attempt(3),
  640. retry=tenacity.retry_unless_exception_type(NameError))
  641. def _retryable_test_with_unless_exception_type_name_attempt_limit(thing):
  642. return thing.go()
  643. @retry(retry=tenacity.retry_unless_exception_type())
  644. def _retryable_test_with_unless_exception_type_no_input(thing):
  645. return thing.go()
  646. @retry(
  647. stop=tenacity.stop_after_attempt(5),
  648. retry=tenacity.retry_if_exception_message(
  649. message=NoCustomErrorAfterCount.derived_message))
  650. def _retryable_test_if_exception_message_message(thing):
  651. return thing.go()
  652. @retry(retry=tenacity.retry_if_not_exception_message(
  653. message=NoCustomErrorAfterCount.derived_message))
  654. def _retryable_test_if_not_exception_message_message(thing):
  655. return thing.go()
  656. @retry(retry=tenacity.retry_if_exception_message(
  657. match=NoCustomErrorAfterCount.derived_message[:3] + ".*"))
  658. def _retryable_test_if_exception_message_match(thing):
  659. return thing.go()
  660. @retry(retry=tenacity.retry_if_not_exception_message(
  661. match=NoCustomErrorAfterCount.derived_message[:3] + ".*"))
  662. def _retryable_test_if_not_exception_message_match(thing):
  663. return thing.go()
  664. @retry(retry=tenacity.retry_if_not_exception_message(
  665. message=NameErrorUntilCount.derived_message))
  666. def _retryable_test_not_exception_message_delay(thing):
  667. return thing.go()
  668. @retry
  669. def _retryable_default(thing):
  670. return thing.go()
  671. @retry()
  672. def _retryable_default_f(thing):
  673. return thing.go()
  674. @retry(retry=tenacity.retry_if_exception_type(CustomError))
  675. def _retryable_test_with_exception_type_custom(thing):
  676. return thing.go()
  677. @retry(
  678. stop=tenacity.stop_after_attempt(3),
  679. retry=tenacity.retry_if_exception_type(CustomError))
  680. def _retryable_test_with_exception_type_custom_attempt_limit(thing):
  681. return thing.go()
  682. class TestDecoratorWrapper(unittest.TestCase):
  683. def test_with_wait(self):
  684. start = current_time_ms()
  685. result = _retryable_test_with_wait(NoneReturnUntilAfterCount(5))
  686. t = current_time_ms() - start
  687. self.assertGreaterEqual(t, 250)
  688. self.assertTrue(result)
  689. def test_retry_with(self):
  690. start = current_time_ms()
  691. result = _retryable_test_with_wait.retry_with(
  692. wait=tenacity.wait_fixed(0.1))(NoneReturnUntilAfterCount(5))
  693. t = current_time_ms() - start
  694. self.assertGreaterEqual(t, 500)
  695. self.assertTrue(result)
  696. def test_with_stop_on_return_value(self):
  697. try:
  698. _retryable_test_with_stop(NoneReturnUntilAfterCount(5))
  699. self.fail("Expected RetryError after 3 attempts")
  700. except RetryError as re:
  701. self.assertFalse(re.last_attempt.failed)
  702. self.assertEqual(3, re.last_attempt.attempt_number)
  703. self.assertTrue(re.last_attempt.result() is None)
  704. print(re)
  705. def test_with_stop_on_exception(self):
  706. try:
  707. _retryable_test_with_stop(NoIOErrorAfterCount(5))
  708. self.fail("Expected IOError")
  709. except IOError as re:
  710. self.assertTrue(isinstance(re, IOError))
  711. print(re)
  712. def test_retry_if_exception_of_type(self):
  713. self.assertTrue(_retryable_test_with_exception_type_io(
  714. NoIOErrorAfterCount(5)))
  715. try:
  716. _retryable_test_with_exception_type_io(NoNameErrorAfterCount(5))
  717. self.fail("Expected NameError")
  718. except NameError as n:
  719. self.assertTrue(isinstance(n, NameError))
  720. print(n)
  721. self.assertTrue(_retryable_test_with_exception_type_custom(
  722. NoCustomErrorAfterCount(5)))
  723. try:
  724. _retryable_test_with_exception_type_custom(
  725. NoNameErrorAfterCount(5))
  726. self.fail("Expected NameError")
  727. except NameError as n:
  728. self.assertTrue(isinstance(n, NameError))
  729. print(n)
  730. def test_retry_until_exception_of_type_attempt_number(self):
  731. try:
  732. self.assertTrue(_retryable_test_with_unless_exception_type_name(
  733. NameErrorUntilCount(5)))
  734. except NameError as e:
  735. s = _retryable_test_with_unless_exception_type_name.\
  736. retry.statistics
  737. self.assertTrue(s['attempt_number'] == 6)
  738. print(e)
  739. else:
  740. self.fail("Expected NameError")
  741. def test_retry_until_exception_of_type_no_type(self):
  742. try:
  743. # no input should catch all subclasses of Exception
  744. self.assertTrue(
  745. _retryable_test_with_unless_exception_type_no_input(
  746. NameErrorUntilCount(5))
  747. )
  748. except NameError as e:
  749. s = _retryable_test_with_unless_exception_type_no_input.\
  750. retry.statistics
  751. self.assertTrue(s['attempt_number'] == 6)
  752. print(e)
  753. else:
  754. self.fail("Expected NameError")
  755. def test_retry_until_exception_of_type_wrong_exception(self):
  756. try:
  757. # two iterations with IOError, one that returns True
  758. _retryable_test_with_unless_exception_type_name_attempt_limit(
  759. IOErrorUntilCount(2))
  760. self.fail("Expected RetryError")
  761. except RetryError as e:
  762. self.assertTrue(isinstance(e, RetryError))
  763. print(e)
  764. def test_retry_if_exception_message(self):
  765. try:
  766. self.assertTrue(_retryable_test_if_exception_message_message(
  767. NoCustomErrorAfterCount(3)))
  768. except CustomError:
  769. print(_retryable_test_if_exception_message_message.retry.
  770. statistics)
  771. self.fail("CustomError should've been retried from errormessage")
  772. def test_retry_if_not_exception_message(self):
  773. try:
  774. self.assertTrue(_retryable_test_if_not_exception_message_message(
  775. NoCustomErrorAfterCount(2)))
  776. except CustomError:
  777. s = _retryable_test_if_not_exception_message_message.retry.\
  778. statistics
  779. self.assertTrue(s['attempt_number'] == 1)
  780. def test_retry_if_not_exception_message_delay(self):
  781. try:
  782. self.assertTrue(_retryable_test_not_exception_message_delay(
  783. NameErrorUntilCount(3)))
  784. except NameError:
  785. s = _retryable_test_not_exception_message_delay.retry.statistics
  786. print(s['attempt_number'])
  787. self.assertTrue(s['attempt_number'] == 4)
  788. def test_retry_if_exception_message_match(self):
  789. try:
  790. self.assertTrue(_retryable_test_if_exception_message_match(
  791. NoCustomErrorAfterCount(3)))
  792. except CustomError:
  793. self.fail("CustomError should've been retried from errormessage")
  794. def test_retry_if_not_exception_message_match(self):
  795. try:
  796. self.assertTrue(_retryable_test_if_not_exception_message_message(
  797. NoCustomErrorAfterCount(2)))
  798. except CustomError:
  799. s = _retryable_test_if_not_exception_message_message.retry.\
  800. statistics
  801. self.assertTrue(s['attempt_number'] == 1)
  802. def test_defaults(self):
  803. self.assertTrue(_retryable_default(NoNameErrorAfterCount(5)))
  804. self.assertTrue(_retryable_default_f(NoNameErrorAfterCount(5)))
  805. self.assertTrue(_retryable_default(NoCustomErrorAfterCount(5)))
  806. self.assertTrue(_retryable_default_f(NoCustomErrorAfterCount(5)))
  807. def test_retry_function_object(self):
  808. """Test that six.wraps doesn't cause problems with callable objects.
  809. It raises an error upon trying to wrap it in Py2, because __name__
  810. attribute is missing. It's fixed in Py3 but was never backported.
  811. """
  812. class Hello(object):
  813. def __call__(self):
  814. return "Hello"
  815. retrying = Retrying(wait=tenacity.wait_fixed(0.01),
  816. stop=tenacity.stop_after_attempt(3))
  817. h = retrying.wraps(Hello())
  818. self.assertEqual(h(), "Hello")
  819. def test_retry_child_class_with_override_backward_compat(self):
  820. def always_true(_):
  821. return True
  822. class MyRetry(tenacity.retry_if_exception):
  823. def __init__(self):
  824. super(MyRetry, self).__init__(always_true)
  825. def __call__(self, attempt):
  826. return super(MyRetry, self).__call__(attempt)
  827. retrying = Retrying(wait=tenacity.wait_fixed(0.01),
  828. stop=tenacity.stop_after_attempt(1),
  829. retry=MyRetry())
  830. def failing():
  831. raise NotImplementedError()
  832. with pytest.raises(RetryError):
  833. retrying.call(failing)
  834. class TestBeforeAfterAttempts(unittest.TestCase):
  835. _attempt_number = 0
  836. def test_before_attempts(self):
  837. TestBeforeAfterAttempts._attempt_number = 0
  838. def _before(retry_state):
  839. TestBeforeAfterAttempts._attempt_number = \
  840. retry_state.attempt_number
  841. @retry(wait=tenacity.wait_fixed(1),
  842. stop=tenacity.stop_after_attempt(1),
  843. before=_before)
  844. def _test_before():
  845. pass
  846. _test_before()
  847. self.assertTrue(TestBeforeAfterAttempts._attempt_number is 1)
  848. def test_after_attempts(self):
  849. TestBeforeAfterAttempts._attempt_number = 0
  850. def _after(retry_state):
  851. TestBeforeAfterAttempts._attempt_number = \
  852. retry_state.attempt_number
  853. @retry(wait=tenacity.wait_fixed(0.1),
  854. stop=tenacity.stop_after_attempt(3),
  855. after=_after)
  856. def _test_after():
  857. if TestBeforeAfterAttempts._attempt_number < 2:
  858. raise Exception("testing after_attempts handler")
  859. else:
  860. pass
  861. _test_after()
  862. self.assertTrue(TestBeforeAfterAttempts._attempt_number is 2)
  863. def test_before_sleep(self):
  864. def _before_sleep(retry_state):
  865. self.assertGreater(retry_state.next_action.sleep, 0)
  866. _before_sleep.attempt_number = retry_state.attempt_number
  867. @retry(wait=tenacity.wait_fixed(0.01),
  868. stop=tenacity.stop_after_attempt(3),
  869. before_sleep=_before_sleep)
  870. def _test_before_sleep():
  871. if _before_sleep.attempt_number < 2:
  872. raise Exception("testing before_sleep_attempts handler")
  873. _test_before_sleep()
  874. self.assertEqual(_before_sleep.attempt_number, 2)
  875. def test_before_sleep_backward_compat(self):
  876. def _before_sleep(retry_obj, sleep, last_result):
  877. self.assertGreater(sleep, 0)
  878. _before_sleep.attempt_number = \
  879. retry_obj.statistics['attempt_number']
  880. _before_sleep.attempt_number = 0
  881. @retry(wait=tenacity.wait_fixed(0.01),
  882. stop=tenacity.stop_after_attempt(3),
  883. before_sleep=_before_sleep)
  884. def _test_before_sleep():
  885. if _before_sleep.attempt_number < 2:
  886. raise Exception("testing before_sleep_attempts handler")
  887. with reports_deprecation_warning():
  888. _test_before_sleep()
  889. self.assertEqual(_before_sleep.attempt_number, 2)
  890. def test_before_sleep_log_raises(self):
  891. thing = NoIOErrorAfterCount(2)
  892. logger = logging.getLogger(self.id())
  893. logger.propagate = False
  894. logger.setLevel(logging.INFO)
  895. handler = CapturingHandler()
  896. logger.addHandler(handler)
  897. try:
  898. _before_sleep = tenacity.before_sleep_log(logger, logging.INFO)
  899. retrying = Retrying(wait=tenacity.wait_fixed(0.01),
  900. stop=tenacity.stop_after_attempt(3),
  901. before_sleep=_before_sleep)
  902. retrying.call(thing.go)
  903. finally:
  904. logger.removeHandler(handler)
  905. etalon_re = r'Retrying .* in 0\.01 seconds as it raised .*\.'
  906. self.assertEqual(len(handler.records), 2)
  907. self.assertRegexpMatches(handler.records[0].getMessage(), etalon_re)
  908. self.assertRegexpMatches(handler.records[1].getMessage(), etalon_re)
  909. def test_before_sleep_log_returns(self):
  910. thing = NoneReturnUntilAfterCount(2)
  911. logger = logging.getLogger(self.id())
  912. logger.propagate = False
  913. logger.setLevel(logging.INFO)
  914. handler = CapturingHandler()
  915. logger.addHandler(handler)
  916. try:
  917. _before_sleep = tenacity.before_sleep_log(logger, logging.INFO)
  918. _retry = tenacity.retry_if_result(lambda result: result is None)
  919. retrying = Retrying(wait=tenacity.wait_fixed(0.01),
  920. stop=tenacity.stop_after_attempt(3),
  921. retry=_retry, before_sleep=_before_sleep)
  922. retrying.call(thing.go)
  923. finally:
  924. logger.removeHandler(handler)
  925. self.assertEqual(len(handler.records), 2)
  926. etalon_re = r'Retrying .* in 0\.01 seconds as it returned None'
  927. self.assertRegexpMatches(handler.records[0].getMessage(), etalon_re)
  928. self.assertRegexpMatches(handler.records[1].getMessage(), etalon_re)
  929. class TestReraiseExceptions(unittest.TestCase):
  930. def test_reraise_by_default(self):
  931. calls = []
  932. @retry(wait=tenacity.wait_fixed(0.1),
  933. stop=tenacity.stop_after_attempt(2),
  934. reraise=True)
  935. def _reraised_by_default():
  936. calls.append('x')
  937. raise KeyError("Bad key")
  938. self.assertRaises(KeyError, _reraised_by_default)
  939. self.assertEqual(2, len(calls))
  940. def test_reraise_from_retry_error(self):
  941. calls = []
  942. @retry(wait=tenacity.wait_fixed(0.1),
  943. stop=tenacity.stop_after_attempt(2))
  944. def _raise_key_error():
  945. calls.append('x')
  946. raise KeyError("Bad key")
  947. def _reraised_key_error():
  948. try:
  949. _raise_key_error()
  950. except tenacity.RetryError as retry_err:
  951. retry_err.reraise()
  952. self.assertRaises(KeyError, _reraised_key_error)
  953. self.assertEqual(2, len(calls))
  954. def test_reraise_timeout_from_retry_error(self):
  955. calls = []
  956. @retry(wait=tenacity.wait_fixed(0.1),
  957. stop=tenacity.stop_after_attempt(2),
  958. retry=lambda retry_state: True)
  959. def _mock_fn():
  960. calls.append('x')
  961. def _reraised_mock_fn():
  962. try:
  963. _mock_fn()
  964. except tenacity.RetryError as retry_err:
  965. retry_err.reraise()
  966. self.assertRaises(tenacity.RetryError, _reraised_mock_fn)
  967. self.assertEqual(2, len(calls))
  968. def test_reraise_no_exception(self):
  969. calls = []
  970. @retry(wait=tenacity.wait_fixed(0.1),
  971. stop=tenacity.stop_after_attempt(2),
  972. retry=lambda retry_state: True,
  973. reraise=True)
  974. def _mock_fn():
  975. calls.append('x')
  976. self.assertRaises(tenacity.RetryError, _mock_fn)
  977. self.assertEqual(2, len(calls))
  978. class TestStatistics(unittest.TestCase):
  979. def test_stats(self):
  980. @retry()
  981. def _foobar():
  982. return 42
  983. self.assertEqual({}, _foobar.retry.statistics)
  984. _foobar()
  985. self.assertEqual(1, _foobar.retry.statistics['attempt_number'])
  986. def test_stats_failing(self):
  987. @retry(stop=tenacity.stop_after_attempt(2))
  988. def _foobar():
  989. raise ValueError(42)
  990. self.assertEqual({}, _foobar.retry.statistics)
  991. try:
  992. _foobar()
  993. except Exception:
  994. pass
  995. self.assertEqual(2, _foobar.retry.statistics['attempt_number'])
  996. class TestRetryErrorCallback(unittest.TestCase):
  997. def setUp(self):
  998. self._attempt_number = 0
  999. self._callback_called = False
  1000. def _callback(self, fut):
  1001. self._callback_called = True
  1002. return fut
  1003. def test_retry_error_callback_backward_compat(self):
  1004. num_attempts = 3
  1005. def retry_error_callback(fut):
  1006. retry_error_callback.called_times += 1
  1007. return fut
  1008. retry_error_callback.called_times = 0
  1009. @retry(stop=tenacity.stop_after_attempt(num_attempts),
  1010. retry_error_callback=retry_error_callback)
  1011. def _foobar():
  1012. self._attempt_number += 1
  1013. raise Exception("This exception should not be raised")
  1014. with reports_deprecation_warning():
  1015. result = _foobar()
  1016. self.assertEqual(retry_error_callback.called_times, 1)
  1017. self.assertEqual(num_attempts, self._attempt_number)
  1018. self.assertIsInstance(result, tenacity.Future)
  1019. def test_retry_error_callback(self):
  1020. num_attempts = 3
  1021. def retry_error_callback(retry_state):
  1022. retry_error_callback.called_times += 1
  1023. return retry_state.outcome
  1024. retry_error_callback.called_times = 0
  1025. @retry(stop=tenacity.stop_after_attempt(num_attempts),
  1026. retry_error_callback=retry_error_callback)
  1027. def _foobar():
  1028. self._attempt_number += 1
  1029. raise Exception("This exception should not be raised")
  1030. result = _foobar()
  1031. self.assertEqual(retry_error_callback.called_times, 1)
  1032. self.assertEqual(num_attempts, self._attempt_number)
  1033. self.assertIsInstance(result, tenacity.Future)
  1034. @contextmanager
  1035. def reports_deprecation_warning():
  1036. __tracebackhide__ = True
  1037. oldfilters = copy(warnings.filters)
  1038. warnings.simplefilter('always')
  1039. try:
  1040. with pytest.warns(DeprecationWarning):
  1041. yield
  1042. finally:
  1043. warnings.filters = oldfilters
  1044. if __name__ == '__main__':
  1045. unittest.main()