1""" 2The functions in this module are meant to run on a separate worker process. 3Exception: in single process mode _execute is called directly. 4 5For efficiency, we copy all data needed to execute all tests into each worker 6and store it in global variables. This reduces the cost of each task. 7""" 8import contextlib 9import os 10import signal 11import time 12import traceback 13 14import lit.Test 15import lit.util 16 17 18_lit_config = None 19_parallelism_semaphores = None 20 21 22def initialize(lit_config, parallelism_semaphores): 23 """Copy data shared by all test executions into worker processes""" 24 global _lit_config 25 global _parallelism_semaphores 26 _lit_config = lit_config 27 _parallelism_semaphores = parallelism_semaphores 28 29 # We use the following strategy for dealing with Ctrl+C/KeyboardInterrupt in 30 # subprocesses created by the multiprocessing.Pool. 31 # https://noswap.com/blog/python-multiprocessing-keyboardinterrupt 32 signal.signal(signal.SIGINT, signal.SIG_IGN) 33 34 35def execute(test): 36 """Run one test in a multiprocessing.Pool 37 38 Side effects in this function and functions it calls are not visible in the 39 main lit process. 40 41 Arguments and results of this function are pickled, so they should be cheap 42 to copy. 43 """ 44 with _get_parallelism_semaphore(test): 45 result = _execute(test, _lit_config) 46 47 test.setResult(result) 48 return test 49 50 51# TODO(python3): replace with contextlib.nullcontext 52@contextlib.contextmanager 53def NopSemaphore(): 54 yield 55 56 57def _get_parallelism_semaphore(test): 58 pg = test.config.parallelism_group 59 if callable(pg): 60 pg = pg(test) 61 return _parallelism_semaphores.get(pg, NopSemaphore()) 62 63 64# Do not inline! Directly used by LitTestCase.py 65def _execute(test, lit_config): 66 start = time.time() 67 result = _execute_test_handle_errors(test, lit_config) 68 result.elapsed = time.time() - start 69 result.start = start 70 result.pid = os.getpid() 71 return result 72 73 74def _execute_test_handle_errors(test, lit_config): 75 try: 76 result = test.config.test_format.execute(test, lit_config) 77 return _adapt_result(result) 78 except: 79 if lit_config.debug: 80 raise 81 output = 'Exception during script execution:\n' 82 output += traceback.format_exc() 83 output += '\n' 84 return lit.Test.Result(lit.Test.UNRESOLVED, output) 85 86 87# Support deprecated result from execute() which returned the result 88# code and additional output as a tuple. 89def _adapt_result(result): 90 if isinstance(result, lit.Test.Result): 91 return result 92 assert isinstance(result, tuple) 93 code, output = result 94 return lit.Test.Result(code, output) 95