mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
158 lines
5.5 KiB
Python
158 lines
5.5 KiB
Python
# 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
|
|
|