|  | # Copyright 2014 The Chromium Authors. All rights reserved. | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  |  | 
|  | """ | 
|  | Function/method decorators that provide timeout and retry logic. | 
|  | """ | 
|  |  | 
|  | import functools | 
|  | import os | 
|  | import sys | 
|  | import threading | 
|  |  | 
|  | from pylib import cmd_helper | 
|  | from pylib import constants | 
|  | from pylib.device import device_errors | 
|  | from pylib.utils import reraiser_thread | 
|  | from pylib.utils import timeout_retry | 
|  |  | 
|  | # TODO(jbudorick) Remove once the DeviceUtils implementations are no longer | 
|  | #                 backed by AndroidCommands / android_testrunner. | 
|  | sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT, 'third_party', | 
|  | 'android_testrunner')) | 
|  | import errors as old_errors | 
|  |  | 
|  | DEFAULT_TIMEOUT_ATTR = '_default_timeout' | 
|  | DEFAULT_RETRIES_ATTR = '_default_retries' | 
|  |  | 
|  |  | 
|  | def _TimeoutRetryWrapper(f, timeout_func, retries_func, pass_values=False): | 
|  | """ Wraps a funcion with timeout and retry handling logic. | 
|  |  | 
|  | Args: | 
|  | f: The function to wrap. | 
|  | timeout_func: A callable that returns the timeout value. | 
|  | retries_func: A callable that returns the retries value. | 
|  | pass_values: If True, passes the values returned by |timeout_func| and | 
|  | |retries_func| to the wrapped function as 'timeout' and | 
|  | 'retries' kwargs, respectively. | 
|  | Returns: | 
|  | The wrapped function. | 
|  | """ | 
|  | @functools.wraps(f) | 
|  | def TimeoutRetryWrapper(*args, **kwargs): | 
|  | timeout = timeout_func(*args, **kwargs) | 
|  | retries = retries_func(*args, **kwargs) | 
|  | if pass_values: | 
|  | kwargs['timeout'] = timeout | 
|  | kwargs['retries'] = retries | 
|  | def impl(): | 
|  | return f(*args, **kwargs) | 
|  | try: | 
|  | if isinstance(threading.current_thread(), | 
|  | timeout_retry.TimeoutRetryThread): | 
|  | return impl() | 
|  | else: | 
|  | return timeout_retry.Run(impl, timeout, retries) | 
|  | except old_errors.WaitForResponseTimedOutError as e: | 
|  | raise device_errors.CommandTimeoutError(str(e)), None, ( | 
|  | sys.exc_info()[2]) | 
|  | except old_errors.DeviceUnresponsiveError as e: | 
|  | raise device_errors.DeviceUnreachableError(str(e)), None, ( | 
|  | sys.exc_info()[2]) | 
|  | except reraiser_thread.TimeoutError as e: | 
|  | raise device_errors.CommandTimeoutError(str(e)), None, ( | 
|  | sys.exc_info()[2]) | 
|  | except cmd_helper.TimeoutError as e: | 
|  | raise device_errors.CommandTimeoutError(str(e)), None, ( | 
|  | sys.exc_info()[2]) | 
|  | return TimeoutRetryWrapper | 
|  |  | 
|  |  | 
|  | def WithTimeoutAndRetries(f): | 
|  | """A decorator that handles timeouts and retries. | 
|  |  | 
|  | 'timeout' and 'retries' kwargs must be passed to the function. | 
|  |  | 
|  | Args: | 
|  | f: The function to decorate. | 
|  | Returns: | 
|  | The decorated function. | 
|  | """ | 
|  | get_timeout = lambda *a, **kw: kw['timeout'] | 
|  | get_retries = lambda *a, **kw: kw['retries'] | 
|  | return _TimeoutRetryWrapper(f, get_timeout, get_retries) | 
|  |  | 
|  |  | 
|  | def WithExplicitTimeoutAndRetries(timeout, retries): | 
|  | """Returns a decorator that handles timeouts and retries. | 
|  |  | 
|  | The provided |timeout| and |retries| values are always used. | 
|  |  | 
|  | Args: | 
|  | timeout: The number of seconds to wait for the decorated function to | 
|  | return. Always used. | 
|  | retries: The number of times the decorated function should be retried on | 
|  | failure. Always used. | 
|  | Returns: | 
|  | The actual decorator. | 
|  | """ | 
|  | def decorator(f): | 
|  | get_timeout = lambda *a, **kw: timeout | 
|  | get_retries = lambda *a, **kw: retries | 
|  | return _TimeoutRetryWrapper(f, get_timeout, get_retries) | 
|  | return decorator | 
|  |  | 
|  |  | 
|  | def WithTimeoutAndRetriesDefaults(default_timeout, default_retries): | 
|  | """Returns a decorator that handles timeouts and retries. | 
|  |  | 
|  | The provided |default_timeout| and |default_retries| values are used only | 
|  | if timeout and retries values are not provided. | 
|  |  | 
|  | Args: | 
|  | default_timeout: The number of seconds to wait for the decorated function | 
|  | to return. Only used if a 'timeout' kwarg is not passed | 
|  | to the decorated function. | 
|  | default_retries: The number of times the decorated function should be | 
|  | retried on failure. Only used if a 'retries' kwarg is not | 
|  | passed to the decorated function. | 
|  | Returns: | 
|  | The actual decorator. | 
|  | """ | 
|  | def decorator(f): | 
|  | get_timeout = lambda *a, **kw: kw.get('timeout', default_timeout) | 
|  | get_retries = lambda *a, **kw: kw.get('retries', default_retries) | 
|  | return _TimeoutRetryWrapper(f, get_timeout, get_retries, pass_values=True) | 
|  | return decorator | 
|  |  | 
|  |  | 
|  | def WithTimeoutAndRetriesFromInstance( | 
|  | default_timeout_name=DEFAULT_TIMEOUT_ATTR, | 
|  | default_retries_name=DEFAULT_RETRIES_ATTR): | 
|  | """Returns a decorator that handles timeouts and retries. | 
|  |  | 
|  | The provided |default_timeout_name| and |default_retries_name| are used to | 
|  | get the default timeout value and the default retries value from the object | 
|  | instance if timeout and retries values are not provided. | 
|  |  | 
|  | Note that this should only be used to decorate methods, not functions. | 
|  |  | 
|  | Args: | 
|  | default_timeout_name: The name of the default timeout attribute of the | 
|  | instance. | 
|  | default_retries_name: The name of the default retries attribute of the | 
|  | instance. | 
|  | Returns: | 
|  | The actual decorator. | 
|  | """ | 
|  | def decorator(f): | 
|  | def get_timeout(inst, *_args, **kwargs): | 
|  | return kwargs.get('timeout', getattr(inst, default_timeout_name)) | 
|  | def get_retries(inst, *_args, **kwargs): | 
|  | return kwargs.get('retries', getattr(inst, default_retries_name)) | 
|  | return _TimeoutRetryWrapper(f, get_timeout, get_retries, pass_values=True) | 
|  | return decorator | 
|  |  |