074a0fcac14131abe15fc3cdbf719c15849f7908
[platform/framework/web/crosswalk.git] / src / build / android / pylib / device / decorators.py
1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """
6 Function/method decorators that provide timeout and retry logic.
7 """
8
9 import functools
10 import os
11 import sys
12 import threading
13
14 from pylib import constants
15 from pylib.device import device_errors
16 from pylib.utils import reraiser_thread
17 from pylib.utils import timeout_retry
18
19 # TODO(jbudorick) Remove once the DeviceUtils implementations are no longer
20 #                 backed by AndroidCommands / android_testrunner.
21 sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT, 'third_party',
22                              'android_testrunner'))
23 import errors as old_errors
24
25 DEFAULT_TIMEOUT_ATTR = '_default_timeout'
26 DEFAULT_RETRIES_ATTR = '_default_retries'
27
28
29 def _TimeoutRetryWrapper(f, timeout_func, retries_func, pass_values=False):
30   """ Wraps a funcion with timeout and retry handling logic.
31
32   Args:
33     f: The function to wrap.
34     timeout_func: A callable that returns the timeout value.
35     retries_func: A callable that returns the retries value.
36     pass_values: If True, passes the values returned by |timeout_func| and
37                  |retries_func| to the wrapped function as 'timeout' and
38                  'retries' kwargs, respectively.
39   Returns:
40     The wrapped function.
41   """
42   @functools.wraps(f)
43   def TimeoutRetryWrapper(*args, **kwargs):
44     timeout = timeout_func(*args, **kwargs)
45     retries = retries_func(*args, **kwargs)
46     if pass_values:
47       kwargs['timeout'] = timeout
48       kwargs['retries'] = retries
49     def impl():
50       return f(*args, **kwargs)
51     try:
52       if isinstance(threading.current_thread(),
53                     timeout_retry.TimeoutRetryThread):
54         return impl()
55       else:
56         return timeout_retry.Run(impl, timeout, retries)
57     except old_errors.WaitForResponseTimedOutError as e:
58       raise device_errors.CommandTimeoutError(str(e)), None, (
59              sys.exc_info()[2])
60     except old_errors.DeviceUnresponsiveError as e:
61       raise device_errors.DeviceUnreachableError(str(e)), None, (
62              sys.exc_info()[2])
63     except reraiser_thread.TimeoutError as e:
64       raise device_errors.CommandTimeoutError(str(e)), None, (
65              sys.exc_info()[2])
66   return TimeoutRetryWrapper
67
68
69 def WithTimeoutAndRetries(f):
70   """A decorator that handles timeouts and retries.
71
72   'timeout' and 'retries' kwargs must be passed to the function.
73
74   Args:
75     f: The function to decorate.
76   Returns:
77     The decorated function.
78   """
79   get_timeout = lambda *a, **kw: kw['timeout']
80   get_retries = lambda *a, **kw: kw['retries']
81   return _TimeoutRetryWrapper(f, get_timeout, get_retries)
82
83
84 def WithExplicitTimeoutAndRetries(timeout, retries):
85   """Returns a decorator that handles timeouts and retries.
86
87   The provided |timeout| and |retries| values are always used.
88
89   Args:
90     timeout: The number of seconds to wait for the decorated function to
91              return. Always used.
92     retries: The number of times the decorated function should be retried on
93              failure. Always used.
94   Returns:
95     The actual decorator.
96   """
97   def decorator(f):
98     get_timeout = lambda *a, **kw: timeout
99     get_retries = lambda *a, **kw: retries
100     return _TimeoutRetryWrapper(f, get_timeout, get_retries)
101   return decorator
102
103
104 def WithTimeoutAndRetriesDefaults(default_timeout, default_retries):
105   """Returns a decorator that handles timeouts and retries.
106
107   The provided |default_timeout| and |default_retries| values are used only
108   if timeout and retries values are not provided.
109
110   Args:
111     default_timeout: The number of seconds to wait for the decorated function
112                      to return. Only used if a 'timeout' kwarg is not passed
113                      to the decorated function.
114     default_retries: The number of times the decorated function should be
115                      retried on failure. Only used if a 'retries' kwarg is not
116                      passed to the decorated function.
117   Returns:
118     The actual decorator.
119   """
120   def decorator(f):
121     get_timeout = lambda *a, **kw: kw.get('timeout', default_timeout)
122     get_retries = lambda *a, **kw: kw.get('retries', default_retries)
123     return _TimeoutRetryWrapper(f, get_timeout, get_retries, pass_values=True)
124   return decorator
125
126
127 def WithTimeoutAndRetriesFromInstance(
128     default_timeout_name=DEFAULT_TIMEOUT_ATTR,
129     default_retries_name=DEFAULT_RETRIES_ATTR):
130   """Returns a decorator that handles timeouts and retries.
131
132   The provided |default_timeout_name| and |default_retries_name| are used to
133   get the default timeout value and the default retries value from the object
134   instance if timeout and retries values are not provided.
135
136   Note that this should only be used to decorate methods, not functions.
137
138   Args:
139     default_timeout_name: The name of the default timeout attribute of the
140                           instance.
141     default_retries_name: The name of the default retries attribute of the
142                           instance.
143   Returns:
144     The actual decorator.
145   """
146   def decorator(f):
147     def get_timeout(inst, *_args, **kwargs):
148       return kwargs.get('timeout', getattr(inst, default_timeout_name))
149     def get_retries(inst, *_args, **kwargs):
150       return kwargs.get('retries', getattr(inst, default_retries_name))
151     return _TimeoutRetryWrapper(f, get_timeout, get_retries, pass_values=True)
152   return decorator
153