1#!/usr/bin/env python 2 3# Copyright (c) 2016 The Chromium Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7from __future__ import print_function 8 9import functools 10import inspect 11import os 12import sys 13import time 14import platform 15 16 17def GetCatapultDir(): 18 return os.path.normpath( 19 os.path.join(os.path.dirname(__file__), '..', '..', '..')) 20 21 22def IsRunningOnCrosDevice(): 23 """Returns True if we're on a ChromeOS device.""" 24 lsb_release = '/etc/lsb-release' 25 if sys.platform.startswith('linux') and os.path.exists(lsb_release): 26 with open(lsb_release, 'r') as f: 27 res = f.read() 28 if res.count('CHROMEOS_RELEASE_NAME'): 29 return True 30 return False 31 32 33def GetHostOsName(): 34 if IsRunningOnCrosDevice(): 35 return 'chromeos' 36 elif sys.platform.startswith('linux'): 37 return 'linux' 38 elif sys.platform == 'darwin': 39 return 'mac' 40 elif sys.platform == 'win32': 41 return 'win' 42 43 44def GetHostArchName(): 45 return platform.machine() 46 47 48def _ExecutableExtensions(): 49 # pathext is, e.g. '.com;.exe;.bat;.cmd' 50 exts = os.getenv('PATHEXT').split(';') #e.g. ['.com','.exe','.bat','.cmd'] 51 return [x[1:].upper() for x in exts] #e.g. ['COM','EXE','BAT','CMD'] 52 53 54def IsExecutable(path): 55 if os.path.isfile(path): 56 if hasattr(os, 'name') and os.name == 'nt': 57 return path.split('.')[-1].upper() in _ExecutableExtensions() 58 else: 59 return os.access(path, os.X_OK) 60 else: 61 return False 62 63 64def _AddDirToPythonPath(*path_parts): 65 path = os.path.abspath(os.path.join(*path_parts)) 66 if os.path.isdir(path) and path not in sys.path: 67 # Some callsite that use telemetry assumes that sys.path[0] is the directory 68 # containing the script, so we add these extra paths to right after it. 69 sys.path.insert(1, path) 70 71_AddDirToPythonPath(os.path.join(GetCatapultDir(), 'devil')) 72_AddDirToPythonPath(os.path.join(GetCatapultDir(), 'dependency_manager')) 73_AddDirToPythonPath(os.path.join(GetCatapultDir(), 'third_party', 'mock')) 74# mox3 is needed for pyfakefs usage, but not for pylint. 75_AddDirToPythonPath(os.path.join(GetCatapultDir(), 'third_party', 'mox3')) 76_AddDirToPythonPath( 77 os.path.join(GetCatapultDir(), 'third_party', 'pyfakefs')) 78 79from devil.utils import timeout_retry # pylint: disable=wrong-import-position 80from devil.utils import reraiser_thread # pylint: disable=wrong-import-position 81 82 83# Decorator that adds timeout functionality to a function. 84def Timeout(default_timeout): 85 return lambda func: TimeoutDeco(func, default_timeout) 86 87# Note: Even though the "timeout" keyword argument is the only 88# keyword argument that will need to be given to the decorated function, 89# we still have to use the **kwargs syntax, because we have to use 90# the *args syntax here before (since the decorator decorates functions 91# with different numbers of positional arguments) and Python doesn't allow 92# a single named keyword argument after *args. 93# (e.g., 'def foo(*args, bar=42):' is a syntax error) 94 95def TimeoutDeco(func, default_timeout): 96 @functools.wraps(func) 97 def RunWithTimeout(*args, **kwargs): 98 if 'timeout' in kwargs: 99 timeout = kwargs['timeout'] 100 else: 101 timeout = default_timeout 102 try: 103 return timeout_retry.Run(func, timeout, 0, args=args) 104 except reraiser_thread.TimeoutError: 105 print('%s timed out.' % func.__name__) 106 return False 107 return RunWithTimeout 108 109 110MIN_POLL_INTERVAL_IN_SECONDS = 0.1 111MAX_POLL_INTERVAL_IN_SECONDS = 5 112OUTPUT_INTERVAL_IN_SECONDS = 300 113 114def WaitFor(condition, timeout): 115 """Waits for up to |timeout| secs for the function |condition| to return True. 116 117 Polling frequency is (elapsed_time / 10), with a min of .1s and max of 5s. 118 119 Returns: 120 Result of |condition| function (if present). 121 """ 122 def GetConditionString(): 123 if condition.__name__ == '<lambda>': 124 try: 125 return inspect.getsource(condition).strip() 126 except IOError: 127 pass 128 return condition.__name__ 129 130 # Do an initial check to see if its true. 131 res = condition() 132 if res: 133 return res 134 start_time = time.time() 135 last_output_time = start_time 136 elapsed_time = time.time() - start_time 137 while elapsed_time < timeout: 138 res = condition() 139 if res: 140 return res 141 now = time.time() 142 elapsed_time = now - start_time 143 last_output_elapsed_time = now - last_output_time 144 if last_output_elapsed_time > OUTPUT_INTERVAL_IN_SECONDS: 145 last_output_time = time.time() 146 poll_interval = min(max(elapsed_time / 10., MIN_POLL_INTERVAL_IN_SECONDS), 147 MAX_POLL_INTERVAL_IN_SECONDS) 148 time.sleep(poll_interval) 149 raise TimeoutException('Timed out while waiting %ds for %s.' % 150 (timeout, GetConditionString())) 151 152class TimeoutException(Exception): 153 """The operation failed to complete because of a timeout. 154 155 It is possible that waiting for a longer period of time would result in a 156 successful operation. 157 """ 158 pass 159