1from __future__ import print_function 2 3import os.path 4import sys 5import time 6import traceback 7 8from .. import Env 9 10_COLOR_CODES = { 11 'white': u'\033[0m', 12 'red': u'\033[31m', 13 'green': u'\033[32m', 14 'orange': u'\033[33m', 15 'blue': u'\033[34m', 16 'purple': u'\033[35m', 17 'W': u'\033[0m', 18 'R': u'\033[31m', 19 'G': u'\033[32m', 20 'O': u'\033[33m', 21 'B': u'\033[34m', 22 'P': u'\033[35m' 23} 24 25 26def _get_log_file_name(): 27 if Env.Env.CURRENT_LOG_FOLDER: 28 file_name = os.path.join(Env.Env.CURRENT_LOG_FOLDER, 'console.log') 29 else: 30 raise OSError('env log folder does not exist, will not save to log file') 31 return file_name 32 33 34def format_timestamp(): 35 ts = time.time() 36 return '{}:{}'.format(time.strftime('%m-%d %H:%M:%S', time.localtime(ts)), str(ts % 1)[2:5]) 37 38 39def console_log(data, color='white', end='\n'): 40 """ 41 log data to console. 42 (if not flush console log, Gitlab-CI won't update logs during job execution) 43 44 :param data: data content 45 :param color: color 46 """ 47 if color not in _COLOR_CODES: 48 color = 'white' 49 color_codes = _COLOR_CODES[color] 50 if isinstance(data, type(b'')): 51 data = data.decode('utf-8', 'replace') 52 print(color_codes + data, end=end) 53 if color not in ['white', 'W']: 54 # reset color to white for later logs 55 print(_COLOR_CODES['white'] + u'\r') 56 sys.stdout.flush() 57 log_data = '[{}] '.format(format_timestamp()) + data 58 try: 59 log_file = _get_log_file_name() 60 with open(log_file, 'a+') as f: 61 f.write(log_data + end) 62 except OSError: 63 pass 64 65 66__LOADED_MODULES = dict() 67# we should only load one module once. 68# if we load one module twice, 69# python will regard the same object loaded in the first time and second time as different objects. 70# it will lead to strange errors like `isinstance(object, type_of_this_object)` return False 71 72 73def load_source(path): 74 """ 75 Dynamic loading python file. Note that this function SHOULD NOT be used to replace ``import``. 76 It should only be used when the package path is only available in runtime. 77 78 :param path: The path of python file 79 :return: Loaded object 80 """ 81 path = os.path.realpath(path) 82 # load name need to be unique, otherwise it will update the already loaded module 83 load_name = str(len(__LOADED_MODULES)) 84 try: 85 return __LOADED_MODULES[path] 86 except KeyError: 87 try: 88 dir = os.path.dirname(path) 89 sys.path.append(dir) 90 from importlib.machinery import SourceFileLoader 91 ret = SourceFileLoader(load_name, path).load_module() 92 except ImportError: 93 # importlib.machinery doesn't exists in Python 2 so we will use imp (deprecated in Python 3) 94 import imp 95 ret = imp.load_source(load_name, path) 96 finally: 97 sys.path.remove(dir) 98 __LOADED_MODULES[path] = ret 99 return ret 100 101 102def handle_unexpected_exception(junit_test_case, exception): 103 """ 104 Helper to log & add junit result details for an unexpected exception encountered 105 when running a test case. 106 107 Should always be called from inside an except: block 108 """ 109 traceback.print_exc() 110 # AssertionError caused by an 'assert' statement has an empty string as its 'str' form 111 e_str = str(exception) if str(exception) else repr(exception) 112 junit_test_case.add_failure_info('Unexpected exception: {}\n{}'.format(e_str, traceback.format_exc())) 113 114 115def format_case_id(case_name, target='esp32', config='default'): 116 return '{}.{}.{}'.format(target, config, case_name) 117