From 8af967169db99b0bf0db69f74859652f144fe896 Mon Sep 17 00:00:00 2001 From: Tim Savannah Date: Sat, 20 May 2017 21:23:37 -0400 Subject: [PATCH] Make python2 work as well as python3 --- func_timeout/StoppableThread.py | 15 +++++++++++++++ tests/FuncTimeoutTests/test_Basic.py | 14 +++++++++++--- tests/FuncTimeoutTests/test_Decorator.py | 17 ++++++++++++----- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/func_timeout/StoppableThread.py b/func_timeout/StoppableThread.py index 9dbc013..ebb66cb 100644 --- a/func_timeout/StoppableThread.py +++ b/func_timeout/StoppableThread.py @@ -9,6 +9,7 @@ import os import ctypes import threading import time +import sys __all__ = ('StoppableThread', 'JoinThread') @@ -24,6 +25,7 @@ class StoppableThread(threading.Thread): self._stderr = open(os.devnull, 'w') joinThread = JoinThread(self, exception) + joinThread._stderr = self._stderr joinThread.start() joinThread._stderr = self._stderr @@ -40,8 +42,21 @@ class JoinThread(threading.Thread): self.daemon = True def run(self): + + # Try to silence default exception printing. + self.otherThread._Thread__stderr = self._stderr + if hasattr(self.otherThread, '_Thread__stop'): + # If py2, call this first to start thread termination cleanly. + # Python3 does not need such ( nor does it provide.. ) + self.otherThread._Thread__stop() while self.otherThread.isAlive(): # We loop raising exception incase it's caught hopefully this breaks us far out. ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(self.otherThread.ident), ctypes.py_object(self.exception)) self.otherThread.join(self.repeatEvery) + try: + self._stderr.close() + except: + pass + + diff --git a/tests/FuncTimeoutTests/test_Basic.py b/tests/FuncTimeoutTests/test_Basic.py index fee6423..3da848f 100755 --- a/tests/FuncTimeoutTests/test_Basic.py +++ b/tests/FuncTimeoutTests/test_Basic.py @@ -112,10 +112,18 @@ class TestBasic(object): assert gotException , 'Expected to time out after .4 seconds when providing .4' assert compareTimes(endTime, startTime, .4, 3, .05, None) == 0 , 'Expected providing .4 would allow timeout of up to .4 seconds' - time.sleep(.1) - gc.collect() + threadsCleanedUp = False - assert threading.active_count() == 1 , 'Expected other threads to get cleaned up after gc collection' + for i in range(5): + time.sleep(1) + gc.collect() + + if threading.active_count() == 1: + threadsCleanedUp = True + break + + + assert threadsCleanedUp , 'Expected other threads to get cleaned up after gc collection' def test_exception(self): sleepFunction = getSleepLambda(.5) diff --git a/tests/FuncTimeoutTests/test_Decorator.py b/tests/FuncTimeoutTests/test_Decorator.py index ff196e5..e60d379 100755 --- a/tests/FuncTimeoutTests/test_Decorator.py +++ b/tests/FuncTimeoutTests/test_Decorator.py @@ -382,12 +382,19 @@ class TestDecorator(object): assert compareTimes(endTime, startTime, SLEEP_TIME , None, .1) == 0 , 'Expected to sleep for 100% SLEEP_TIME with 80% timeout overriden on retry ( SLEEP_TIME * 1.5 ) [ 150% timeout ]' assert result == expected - time.sleep(.1) - gc.collect() + threadsCleanedUp = False + + for i in range(5): + time.sleep(1) + gc.collect() + + if threading.active_count() == 1: + threadsCleanedUp = True + break + + + assert threadsCleanedUp , 'Expected other threads to get cleaned up after gc collection' - assert threading.active_count() == 1 , 'Expected other threads to get cleaned up after gc collection' - - if __name__ == '__main__': sys.exit(subprocess.Popen('GoodTests.py -n1 "%s" %s' %(sys.argv[0], ' '.join(['"%s"' %(arg.replace('"', '\\"'), ) for arg in sys.argv[1:]]) ), shell=True).wait())