Revision 79d22269
b/Makefile.am | ||
---|---|---|
213 | 213 |
|
214 | 214 |
utils_PYTHON = \ |
215 | 215 |
lib/utils/__init__.py \ |
216 |
lib/utils/algo.py |
|
216 |
lib/utils/algo.py \ |
|
217 |
lib/utils/retry.py |
|
217 | 218 |
|
218 | 219 |
docrst = \ |
219 | 220 |
doc/admin.rst \ |
... | ... | |
480 | 481 |
test/ganeti.ssh_unittest.py \ |
481 | 482 |
test/ganeti.uidpool_unittest.py \ |
482 | 483 |
test/ganeti.utils.algo_unittest.py \ |
484 |
test/ganeti.utils.retry_unittest.py \ |
|
483 | 485 |
test/ganeti.utils_mlockall_unittest.py \ |
484 | 486 |
test/ganeti.utils_unittest.py \ |
485 | 487 |
test/ganeti.workerpool_unittest.py \ |
b/lib/utils/__init__.py | ||
---|---|---|
63 | 63 |
from ganeti import compat |
64 | 64 |
|
65 | 65 |
from ganeti.utils.algo import * # pylint: disable-msg=W0401 |
66 |
from ganeti.utils.retry import * # pylint: disable-msg=W0401 |
|
66 | 67 |
|
67 | 68 |
_locksheld = [] |
68 | 69 |
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$') |
... | ... | |
3353 | 3354 |
return value |
3354 | 3355 |
|
3355 | 3356 |
|
3356 |
class RetryTimeout(Exception): |
|
3357 |
"""Retry loop timed out. |
|
3358 |
|
|
3359 |
Any arguments which was passed by the retried function to RetryAgain will be |
|
3360 |
preserved in RetryTimeout, if it is raised. If such argument was an exception |
|
3361 |
the RaiseInner helper method will reraise it. |
|
3362 |
|
|
3363 |
""" |
|
3364 |
def RaiseInner(self): |
|
3365 |
if self.args and isinstance(self.args[0], Exception): |
|
3366 |
raise self.args[0] |
|
3367 |
else: |
|
3368 |
raise RetryTimeout(*self.args) |
|
3369 |
|
|
3370 |
|
|
3371 |
class RetryAgain(Exception): |
|
3372 |
"""Retry again. |
|
3373 |
|
|
3374 |
Any arguments passed to RetryAgain will be preserved, if a timeout occurs, as |
|
3375 |
arguments to RetryTimeout. If an exception is passed, the RaiseInner() method |
|
3376 |
of the RetryTimeout() method can be used to reraise it. |
|
3377 |
|
|
3378 |
""" |
|
3379 |
|
|
3380 |
|
|
3381 |
class _RetryDelayCalculator(object): |
|
3382 |
"""Calculator for increasing delays. |
|
3383 |
|
|
3384 |
""" |
|
3385 |
__slots__ = [ |
|
3386 |
"_factor", |
|
3387 |
"_limit", |
|
3388 |
"_next", |
|
3389 |
"_start", |
|
3390 |
] |
|
3391 |
|
|
3392 |
def __init__(self, start, factor, limit): |
|
3393 |
"""Initializes this class. |
|
3394 |
|
|
3395 |
@type start: float |
|
3396 |
@param start: Initial delay |
|
3397 |
@type factor: float |
|
3398 |
@param factor: Factor for delay increase |
|
3399 |
@type limit: float or None |
|
3400 |
@param limit: Upper limit for delay or None for no limit |
|
3401 |
|
|
3402 |
""" |
|
3403 |
assert start > 0.0 |
|
3404 |
assert factor >= 1.0 |
|
3405 |
assert limit is None or limit >= 0.0 |
|
3406 |
|
|
3407 |
self._start = start |
|
3408 |
self._factor = factor |
|
3409 |
self._limit = limit |
|
3410 |
|
|
3411 |
self._next = start |
|
3412 |
|
|
3413 |
def __call__(self): |
|
3414 |
"""Returns current delay and calculates the next one. |
|
3415 |
|
|
3416 |
""" |
|
3417 |
current = self._next |
|
3418 |
|
|
3419 |
# Update for next run |
|
3420 |
if self._limit is None or self._next < self._limit: |
|
3421 |
self._next = min(self._limit, self._next * self._factor) |
|
3422 |
|
|
3423 |
return current |
|
3424 |
|
|
3425 |
|
|
3426 |
#: Special delay to specify whole remaining timeout |
|
3427 |
RETRY_REMAINING_TIME = object() |
|
3428 |
|
|
3429 |
|
|
3430 |
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep, |
|
3431 |
_time_fn=time.time): |
|
3432 |
"""Call a function repeatedly until it succeeds. |
|
3433 |
|
|
3434 |
The function C{fn} is called repeatedly until it doesn't throw L{RetryAgain} |
|
3435 |
anymore. Between calls a delay, specified by C{delay}, is inserted. After a |
|
3436 |
total of C{timeout} seconds, this function throws L{RetryTimeout}. |
|
3437 |
|
|
3438 |
C{delay} can be one of the following: |
|
3439 |
- callable returning the delay length as a float |
|
3440 |
- Tuple of (start, factor, limit) |
|
3441 |
- L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is |
|
3442 |
useful when overriding L{wait_fn} to wait for an external event) |
|
3443 |
- A static delay as a number (int or float) |
|
3444 |
|
|
3445 |
@type fn: callable |
|
3446 |
@param fn: Function to be called |
|
3447 |
@param delay: Either a callable (returning the delay), a tuple of (start, |
|
3448 |
factor, limit) (see L{_RetryDelayCalculator}), |
|
3449 |
L{RETRY_REMAINING_TIME} or a number (int or float) |
|
3450 |
@type timeout: float |
|
3451 |
@param timeout: Total timeout |
|
3452 |
@type wait_fn: callable |
|
3453 |
@param wait_fn: Waiting function |
|
3454 |
@return: Return value of function |
|
3455 |
|
|
3456 |
""" |
|
3457 |
assert callable(fn) |
|
3458 |
assert callable(wait_fn) |
|
3459 |
assert callable(_time_fn) |
|
3460 |
|
|
3461 |
if args is None: |
|
3462 |
args = [] |
|
3463 |
|
|
3464 |
end_time = _time_fn() + timeout |
|
3465 |
|
|
3466 |
if callable(delay): |
|
3467 |
# External function to calculate delay |
|
3468 |
calc_delay = delay |
|
3469 |
|
|
3470 |
elif isinstance(delay, (tuple, list)): |
|
3471 |
# Increasing delay with optional upper boundary |
|
3472 |
(start, factor, limit) = delay |
|
3473 |
calc_delay = _RetryDelayCalculator(start, factor, limit) |
|
3474 |
|
|
3475 |
elif delay is RETRY_REMAINING_TIME: |
|
3476 |
# Always use the remaining time |
|
3477 |
calc_delay = None |
|
3478 |
|
|
3479 |
else: |
|
3480 |
# Static delay |
|
3481 |
calc_delay = lambda: delay |
|
3482 |
|
|
3483 |
assert calc_delay is None or callable(calc_delay) |
|
3484 |
|
|
3485 |
while True: |
|
3486 |
retry_args = [] |
|
3487 |
try: |
|
3488 |
# pylint: disable-msg=W0142 |
|
3489 |
return fn(*args) |
|
3490 |
except RetryAgain, err: |
|
3491 |
retry_args = err.args |
|
3492 |
except RetryTimeout: |
|
3493 |
raise errors.ProgrammerError("Nested retry loop detected that didn't" |
|
3494 |
" handle RetryTimeout") |
|
3495 |
|
|
3496 |
remaining_time = end_time - _time_fn() |
|
3497 |
|
|
3498 |
if remaining_time < 0.0: |
|
3499 |
# pylint: disable-msg=W0142 |
|
3500 |
raise RetryTimeout(*retry_args) |
|
3501 |
|
|
3502 |
assert remaining_time >= 0.0 |
|
3503 |
|
|
3504 |
if calc_delay is None: |
|
3505 |
wait_fn(remaining_time) |
|
3506 |
else: |
|
3507 |
current_delay = calc_delay() |
|
3508 |
if current_delay > 0.0: |
|
3509 |
wait_fn(current_delay) |
|
3510 |
|
|
3511 |
|
|
3512 | 3357 |
def GetClosedTempfile(*args, **kwargs): |
3513 | 3358 |
"""Creates a temporary file and returns its path. |
3514 | 3359 |
|
b/lib/utils/retry.py | ||
---|---|---|
1 |
# |
|
2 |
# |
|
3 |
|
|
4 |
# Copyright (C) 2006, 2007, 2010, 2011 Google Inc. |
|
5 |
# |
|
6 |
# This program is free software; you can redistribute it and/or modify |
|
7 |
# it under the terms of the GNU General Public License as published by |
|
8 |
# the Free Software Foundation; either version 2 of the License, or |
|
9 |
# (at your option) any later version. |
|
10 |
# |
|
11 |
# This program is distributed in the hope that it will be useful, but |
|
12 |
# WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 |
# General Public License for more details. |
|
15 |
# |
|
16 |
# You should have received a copy of the GNU General Public License |
|
17 |
# along with this program; if not, write to the Free Software |
|
18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
19 |
# 02110-1301, USA. |
|
20 |
|
|
21 |
"""Utility functions for retrying function calls with a timeout. |
|
22 |
|
|
23 |
""" |
|
24 |
|
|
25 |
|
|
26 |
import time |
|
27 |
|
|
28 |
from ganeti import errors |
|
29 |
|
|
30 |
|
|
31 |
#: Special delay to specify whole remaining timeout |
|
32 |
RETRY_REMAINING_TIME = object() |
|
33 |
|
|
34 |
|
|
35 |
class RetryTimeout(Exception): |
|
36 |
"""Retry loop timed out. |
|
37 |
|
|
38 |
Any arguments which was passed by the retried function to RetryAgain will be |
|
39 |
preserved in RetryTimeout, if it is raised. If such argument was an exception |
|
40 |
the RaiseInner helper method will reraise it. |
|
41 |
|
|
42 |
""" |
|
43 |
def RaiseInner(self): |
|
44 |
if self.args and isinstance(self.args[0], Exception): |
|
45 |
raise self.args[0] |
|
46 |
else: |
|
47 |
raise RetryTimeout(*self.args) |
|
48 |
|
|
49 |
|
|
50 |
class RetryAgain(Exception): |
|
51 |
"""Retry again. |
|
52 |
|
|
53 |
Any arguments passed to RetryAgain will be preserved, if a timeout occurs, as |
|
54 |
arguments to RetryTimeout. If an exception is passed, the RaiseInner() method |
|
55 |
of the RetryTimeout() method can be used to reraise it. |
|
56 |
|
|
57 |
""" |
|
58 |
|
|
59 |
|
|
60 |
class _RetryDelayCalculator(object): |
|
61 |
"""Calculator for increasing delays. |
|
62 |
|
|
63 |
""" |
|
64 |
__slots__ = [ |
|
65 |
"_factor", |
|
66 |
"_limit", |
|
67 |
"_next", |
|
68 |
"_start", |
|
69 |
] |
|
70 |
|
|
71 |
def __init__(self, start, factor, limit): |
|
72 |
"""Initializes this class. |
|
73 |
|
|
74 |
@type start: float |
|
75 |
@param start: Initial delay |
|
76 |
@type factor: float |
|
77 |
@param factor: Factor for delay increase |
|
78 |
@type limit: float or None |
|
79 |
@param limit: Upper limit for delay or None for no limit |
|
80 |
|
|
81 |
""" |
|
82 |
assert start > 0.0 |
|
83 |
assert factor >= 1.0 |
|
84 |
assert limit is None or limit >= 0.0 |
|
85 |
|
|
86 |
self._start = start |
|
87 |
self._factor = factor |
|
88 |
self._limit = limit |
|
89 |
|
|
90 |
self._next = start |
|
91 |
|
|
92 |
def __call__(self): |
|
93 |
"""Returns current delay and calculates the next one. |
|
94 |
|
|
95 |
""" |
|
96 |
current = self._next |
|
97 |
|
|
98 |
# Update for next run |
|
99 |
if self._limit is None or self._next < self._limit: |
|
100 |
self._next = min(self._limit, self._next * self._factor) |
|
101 |
|
|
102 |
return current |
|
103 |
|
|
104 |
|
|
105 |
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep, |
|
106 |
_time_fn=time.time): |
|
107 |
"""Call a function repeatedly until it succeeds. |
|
108 |
|
|
109 |
The function C{fn} is called repeatedly until it doesn't throw L{RetryAgain} |
|
110 |
anymore. Between calls a delay, specified by C{delay}, is inserted. After a |
|
111 |
total of C{timeout} seconds, this function throws L{RetryTimeout}. |
|
112 |
|
|
113 |
C{delay} can be one of the following: |
|
114 |
- callable returning the delay length as a float |
|
115 |
- Tuple of (start, factor, limit) |
|
116 |
- L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is |
|
117 |
useful when overriding L{wait_fn} to wait for an external event) |
|
118 |
- A static delay as a number (int or float) |
|
119 |
|
|
120 |
@type fn: callable |
|
121 |
@param fn: Function to be called |
|
122 |
@param delay: Either a callable (returning the delay), a tuple of (start, |
|
123 |
factor, limit) (see L{_RetryDelayCalculator}), |
|
124 |
L{RETRY_REMAINING_TIME} or a number (int or float) |
|
125 |
@type timeout: float |
|
126 |
@param timeout: Total timeout |
|
127 |
@type wait_fn: callable |
|
128 |
@param wait_fn: Waiting function |
|
129 |
@return: Return value of function |
|
130 |
|
|
131 |
""" |
|
132 |
assert callable(fn) |
|
133 |
assert callable(wait_fn) |
|
134 |
assert callable(_time_fn) |
|
135 |
|
|
136 |
if args is None: |
|
137 |
args = [] |
|
138 |
|
|
139 |
end_time = _time_fn() + timeout |
|
140 |
|
|
141 |
if callable(delay): |
|
142 |
# External function to calculate delay |
|
143 |
calc_delay = delay |
|
144 |
|
|
145 |
elif isinstance(delay, (tuple, list)): |
|
146 |
# Increasing delay with optional upper boundary |
|
147 |
(start, factor, limit) = delay |
|
148 |
calc_delay = _RetryDelayCalculator(start, factor, limit) |
|
149 |
|
|
150 |
elif delay is RETRY_REMAINING_TIME: |
|
151 |
# Always use the remaining time |
|
152 |
calc_delay = None |
|
153 |
|
|
154 |
else: |
|
155 |
# Static delay |
|
156 |
calc_delay = lambda: delay |
|
157 |
|
|
158 |
assert calc_delay is None or callable(calc_delay) |
|
159 |
|
|
160 |
while True: |
|
161 |
retry_args = [] |
|
162 |
try: |
|
163 |
# pylint: disable-msg=W0142 |
|
164 |
return fn(*args) |
|
165 |
except RetryAgain, err: |
|
166 |
retry_args = err.args |
|
167 |
except RetryTimeout: |
|
168 |
raise errors.ProgrammerError("Nested retry loop detected that didn't" |
|
169 |
" handle RetryTimeout") |
|
170 |
|
|
171 |
remaining_time = end_time - _time_fn() |
|
172 |
|
|
173 |
if remaining_time < 0.0: |
|
174 |
# pylint: disable-msg=W0142 |
|
175 |
raise RetryTimeout(*retry_args) |
|
176 |
|
|
177 |
assert remaining_time >= 0.0 |
|
178 |
|
|
179 |
if calc_delay is None: |
|
180 |
wait_fn(remaining_time) |
|
181 |
else: |
|
182 |
current_delay = calc_delay() |
|
183 |
if current_delay > 0.0: |
|
184 |
wait_fn(current_delay) |
b/test/ganeti.utils.retry_unittest.py | ||
---|---|---|
1 |
#!/usr/bin/python |
|
2 |
# |
|
3 |
|
|
4 |
# Copyright (C) 2011 Google Inc. |
|
5 |
# |
|
6 |
# This program is free software; you can redistribute it and/or modify |
|
7 |
# it under the terms of the GNU General Public License as published by |
|
8 |
# the Free Software Foundation; either version 2 of the License, or |
|
9 |
# (at your option) any later version. |
|
10 |
# |
|
11 |
# This program is distributed in the hope that it will be useful, but |
|
12 |
# WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 |
# General Public License for more details. |
|
15 |
# |
|
16 |
# You should have received a copy of the GNU General Public License |
|
17 |
# along with this program; if not, write to the Free Software |
|
18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
19 |
# 02110-1301, USA. |
|
20 |
|
|
21 |
|
|
22 |
"""Script for testing ganeti.utils.retry""" |
|
23 |
|
|
24 |
import unittest |
|
25 |
|
|
26 |
from ganeti import constants |
|
27 |
from ganeti import errors |
|
28 |
from ganeti import utils |
|
29 |
|
|
30 |
import testutils |
|
31 |
|
|
32 |
|
|
33 |
class TestRetry(testutils.GanetiTestCase): |
|
34 |
def setUp(self): |
|
35 |
testutils.GanetiTestCase.setUp(self) |
|
36 |
self.retries = 0 |
|
37 |
|
|
38 |
@staticmethod |
|
39 |
def _RaiseRetryAgain(): |
|
40 |
raise utils.RetryAgain() |
|
41 |
|
|
42 |
@staticmethod |
|
43 |
def _RaiseRetryAgainWithArg(args): |
|
44 |
raise utils.RetryAgain(*args) |
|
45 |
|
|
46 |
def _WrongNestedLoop(self): |
|
47 |
return utils.Retry(self._RaiseRetryAgain, 0.01, 0.02) |
|
48 |
|
|
49 |
def _RetryAndSucceed(self, retries): |
|
50 |
if self.retries < retries: |
|
51 |
self.retries += 1 |
|
52 |
raise utils.RetryAgain() |
|
53 |
else: |
|
54 |
return True |
|
55 |
|
|
56 |
def testRaiseTimeout(self): |
|
57 |
self.failUnlessRaises(utils.RetryTimeout, utils.Retry, |
|
58 |
self._RaiseRetryAgain, 0.01, 0.02) |
|
59 |
self.failUnlessRaises(utils.RetryTimeout, utils.Retry, |
|
60 |
self._RetryAndSucceed, 0.01, 0, args=[1]) |
|
61 |
self.failUnlessEqual(self.retries, 1) |
|
62 |
|
|
63 |
def testComplete(self): |
|
64 |
self.failUnlessEqual(utils.Retry(lambda: True, 0, 1), True) |
|
65 |
self.failUnlessEqual(utils.Retry(self._RetryAndSucceed, 0, 1, args=[2]), |
|
66 |
True) |
|
67 |
self.failUnlessEqual(self.retries, 2) |
|
68 |
|
|
69 |
def testNestedLoop(self): |
|
70 |
try: |
|
71 |
self.failUnlessRaises(errors.ProgrammerError, utils.Retry, |
|
72 |
self._WrongNestedLoop, 0, 1) |
|
73 |
except utils.RetryTimeout: |
|
74 |
self.fail("Didn't detect inner loop's exception") |
|
75 |
|
|
76 |
def testTimeoutArgument(self): |
|
77 |
retry_arg="my_important_debugging_message" |
|
78 |
try: |
|
79 |
utils.Retry(self._RaiseRetryAgainWithArg, 0.01, 0.02, args=[[retry_arg]]) |
|
80 |
except utils.RetryTimeout, err: |
|
81 |
self.failUnlessEqual(err.args, (retry_arg, )) |
|
82 |
else: |
|
83 |
self.fail("Expected timeout didn't happen") |
|
84 |
|
|
85 |
def testRaiseInnerWithExc(self): |
|
86 |
retry_arg="my_important_debugging_message" |
|
87 |
try: |
|
88 |
try: |
|
89 |
utils.Retry(self._RaiseRetryAgainWithArg, 0.01, 0.02, |
|
90 |
args=[[errors.GenericError(retry_arg, retry_arg)]]) |
|
91 |
except utils.RetryTimeout, err: |
|
92 |
err.RaiseInner() |
|
93 |
else: |
|
94 |
self.fail("Expected timeout didn't happen") |
|
95 |
except errors.GenericError, err: |
|
96 |
self.failUnlessEqual(err.args, (retry_arg, retry_arg)) |
|
97 |
else: |
|
98 |
self.fail("Expected GenericError didn't happen") |
|
99 |
|
|
100 |
def testRaiseInnerWithMsg(self): |
|
101 |
retry_arg="my_important_debugging_message" |
|
102 |
try: |
|
103 |
try: |
|
104 |
utils.Retry(self._RaiseRetryAgainWithArg, 0.01, 0.02, |
|
105 |
args=[[retry_arg, retry_arg]]) |
|
106 |
except utils.RetryTimeout, err: |
|
107 |
err.RaiseInner() |
|
108 |
else: |
|
109 |
self.fail("Expected timeout didn't happen") |
|
110 |
except utils.RetryTimeout, err: |
|
111 |
self.failUnlessEqual(err.args, (retry_arg, retry_arg)) |
|
112 |
else: |
|
113 |
self.fail("Expected RetryTimeout didn't happen") |
|
114 |
|
|
115 |
|
|
116 |
if __name__ == "__main__": |
|
117 |
testutils.GanetiTestProgram() |
b/test/ganeti.utils_unittest.py | ||
---|---|---|
1995 | 1995 |
self.assert_(os.path.isdir(path)) |
1996 | 1996 |
|
1997 | 1997 |
|
1998 |
class TestRetry(testutils.GanetiTestCase): |
|
1999 |
def setUp(self): |
|
2000 |
testutils.GanetiTestCase.setUp(self) |
|
2001 |
self.retries = 0 |
|
2002 |
|
|
2003 |
@staticmethod |
|
2004 |
def _RaiseRetryAgain(): |
|
2005 |
raise utils.RetryAgain() |
|
2006 |
|
|
2007 |
@staticmethod |
|
2008 |
def _RaiseRetryAgainWithArg(args): |
|
2009 |
raise utils.RetryAgain(*args) |
|
2010 |
|
|
2011 |
def _WrongNestedLoop(self): |
|
2012 |
return utils.Retry(self._RaiseRetryAgain, 0.01, 0.02) |
|
2013 |
|
|
2014 |
def _RetryAndSucceed(self, retries): |
|
2015 |
if self.retries < retries: |
|
2016 |
self.retries += 1 |
|
2017 |
raise utils.RetryAgain() |
|
2018 |
else: |
|
2019 |
return True |
|
2020 |
|
|
2021 |
def testRaiseTimeout(self): |
|
2022 |
self.failUnlessRaises(utils.RetryTimeout, utils.Retry, |
|
2023 |
self._RaiseRetryAgain, 0.01, 0.02) |
|
2024 |
self.failUnlessRaises(utils.RetryTimeout, utils.Retry, |
|
2025 |
self._RetryAndSucceed, 0.01, 0, args=[1]) |
|
2026 |
self.failUnlessEqual(self.retries, 1) |
|
2027 |
|
|
2028 |
def testComplete(self): |
|
2029 |
self.failUnlessEqual(utils.Retry(lambda: True, 0, 1), True) |
|
2030 |
self.failUnlessEqual(utils.Retry(self._RetryAndSucceed, 0, 1, args=[2]), |
|
2031 |
True) |
|
2032 |
self.failUnlessEqual(self.retries, 2) |
|
2033 |
|
|
2034 |
def testNestedLoop(self): |
|
2035 |
try: |
|
2036 |
self.failUnlessRaises(errors.ProgrammerError, utils.Retry, |
|
2037 |
self._WrongNestedLoop, 0, 1) |
|
2038 |
except utils.RetryTimeout: |
|
2039 |
self.fail("Didn't detect inner loop's exception") |
|
2040 |
|
|
2041 |
def testTimeoutArgument(self): |
|
2042 |
retry_arg="my_important_debugging_message" |
|
2043 |
try: |
|
2044 |
utils.Retry(self._RaiseRetryAgainWithArg, 0.01, 0.02, args=[[retry_arg]]) |
|
2045 |
except utils.RetryTimeout, err: |
|
2046 |
self.failUnlessEqual(err.args, (retry_arg, )) |
|
2047 |
else: |
|
2048 |
self.fail("Expected timeout didn't happen") |
|
2049 |
|
|
2050 |
def testRaiseInnerWithExc(self): |
|
2051 |
retry_arg="my_important_debugging_message" |
|
2052 |
try: |
|
2053 |
try: |
|
2054 |
utils.Retry(self._RaiseRetryAgainWithArg, 0.01, 0.02, |
|
2055 |
args=[[errors.GenericError(retry_arg, retry_arg)]]) |
|
2056 |
except utils.RetryTimeout, err: |
|
2057 |
err.RaiseInner() |
|
2058 |
else: |
|
2059 |
self.fail("Expected timeout didn't happen") |
|
2060 |
except errors.GenericError, err: |
|
2061 |
self.failUnlessEqual(err.args, (retry_arg, retry_arg)) |
|
2062 |
else: |
|
2063 |
self.fail("Expected GenericError didn't happen") |
|
2064 |
|
|
2065 |
def testRaiseInnerWithMsg(self): |
|
2066 |
retry_arg="my_important_debugging_message" |
|
2067 |
try: |
|
2068 |
try: |
|
2069 |
utils.Retry(self._RaiseRetryAgainWithArg, 0.01, 0.02, |
|
2070 |
args=[[retry_arg, retry_arg]]) |
|
2071 |
except utils.RetryTimeout, err: |
|
2072 |
err.RaiseInner() |
|
2073 |
else: |
|
2074 |
self.fail("Expected timeout didn't happen") |
|
2075 |
except utils.RetryTimeout, err: |
|
2076 |
self.failUnlessEqual(err.args, (retry_arg, retry_arg)) |
|
2077 |
else: |
|
2078 |
self.fail("Expected RetryTimeout didn't happen") |
|
2079 |
|
|
2080 |
|
|
2081 | 1998 |
class TestLineSplitter(unittest.TestCase): |
2082 | 1999 |
def test(self): |
2083 | 2000 |
lines = [] |
Also available in: Unified diff