-
-
Notifications
You must be signed in to change notification settings - Fork 34k
[WIP][2.7] bpo-30351: regrtest: add --timeout #2317
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -186,6 +186,10 @@ | |
| import imp | ||
| import platform | ||
| import sysconfig | ||
| try: | ||
| import threading | ||
| except ImportError: | ||
| threading = None | ||
|
|
||
|
|
||
| # Some times __path__ and __file__ are not absolute (e.g. while running from | ||
|
|
@@ -315,6 +319,8 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, | |
| directly to set the values that would normally be set by flags | ||
| on the command line. | ||
| """ | ||
| watchdog = None | ||
|
|
||
| regrtest_start_time = time.time() | ||
|
|
||
| test_support.record_original_stdout(sys.stdout) | ||
|
|
@@ -327,7 +333,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, | |
| 'runleaks', 'huntrleaks=', 'memlimit=', 'randseed=', | ||
| 'multiprocess=', 'slaveargs=', 'forever', 'header', 'pgo', | ||
| 'failfast', 'match=', 'testdir=', 'list-tests', 'list-cases', | ||
| 'coverage', 'matchfile=']) | ||
| 'coverage', 'matchfile=', 'timeout=']) | ||
| except getopt.error, msg: | ||
| usage(2, msg) | ||
|
|
||
|
|
@@ -339,6 +345,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, | |
| slaveargs = None | ||
| list_tests = False | ||
| list_cases_opt = False | ||
| timeout = None | ||
| for o, a in opts: | ||
| if o in ('-h', '--help'): | ||
| usage(0) | ||
|
|
@@ -376,6 +383,8 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, | |
| with open(filename) as fp: | ||
| for line in fp: | ||
| match_tests.append(line.strip()) | ||
| elif o == '--timeout': | ||
| timeout = float(a) | ||
| elif o in ('-l', '--findleaks'): | ||
| findleaks = True | ||
| elif o in ('-L', '--runleaks'): | ||
|
|
@@ -452,6 +461,14 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, | |
| if failfast and not (verbose or verbose3): | ||
| usage("-G/--failfast needs either -v or -W") | ||
|
|
||
| if timeout is not None: | ||
| if threading is None: | ||
| print("ERROR: --timeout needs threading support") | ||
| sys.exit(1) | ||
|
|
||
| watchdog = WatchDogThread(timeout) | ||
| watchdog.start() | ||
|
|
||
| if testdir: | ||
| testdir = os.path.abspath(testdir) | ||
|
|
||
|
|
@@ -772,6 +789,9 @@ def get_running(workers): | |
| display_progress(test_index, text) | ||
|
|
||
| def local_runtest(): | ||
| if watchdog is not None: | ||
| watchdog.reset() | ||
|
|
||
| result = runtest(test, verbose, quiet, huntrleaks, None, pgo, | ||
| failfast=failfast, | ||
| match_tests=match_tests, | ||
|
|
@@ -793,6 +813,8 @@ def local_runtest(): | |
| if verbose3 and result[0] == FAILED: | ||
| if not pgo: | ||
| print "Re-running test %r in verbose mode" % test | ||
| if watchdog is not None: | ||
| watchdog.reset() | ||
| runtest(test, True, quiet, huntrleaks, None, pgo, | ||
| testdir=testdir) | ||
| except KeyboardInterrupt: | ||
|
|
@@ -871,6 +893,8 @@ def local_runtest(): | |
| print "Re-running test %r in verbose mode" % test | ||
| sys.stdout.flush() | ||
| try: | ||
| if watchdog is not None: | ||
| watchdog.reset() | ||
| test_support.verbose = True | ||
| ok = runtest(test, True, quiet, huntrleaks, None, pgo, | ||
| testdir=testdir) | ||
|
|
@@ -900,6 +924,10 @@ def local_runtest(): | |
| if runleaks: | ||
| os.system("leaks %d" % os.getpid()) | ||
|
|
||
| if watchdog is not None: | ||
| watchdog.stop() | ||
| watchdog.stop() | ||
|
|
||
| duration = time.time() - regrtest_start_time | ||
| print("Total duration: %s" % format_duration(duration)) | ||
|
|
@@ -1910,6 +1938,70 @@ def getexpected(self): | |
| assert self.isvalid() | ||
| return self.expected | ||
|
|
||
|
|
||
| if threading is not None: | ||
| class WatchDogThread(threading.Thread): | ||
| daemon = True | ||
|
|
||
| def __init__(self, timeout): | ||
| threading.Thread.__init__(self) | ||
| self.timeout = timeout | ||
| self.stream = sys.__stdout__ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't Can't standard streams be |
||
| self.quit = False | ||
| self.reset() | ||
|
|
||
| def reset(self): | ||
| self.deadline = time.time() + self.timeout | ||
|
|
||
| def write(self, line): | ||
| self.stream.write(line + "\n") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe |
||
| self.stream.flush() | ||
|
|
||
| def run(self): | ||
| while True: | ||
| # the sleep duration impacts the delay of .join() | ||
| # called by .stop() | ||
| time.sleep(0.1) | ||
| if self.quit: | ||
| return | ||
| if time.time() >= self.deadline: | ||
| break | ||
|
|
||
| try: | ||
| self.dump_threads() | ||
| except: | ||
| self.write("FAILED TO DUMP THREADS") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Print a traceback? |
||
| os._exit(1) | ||
|
|
||
| def stop(self): | ||
| if not self.is_alive(): | ||
| return | ||
|
|
||
| print("Stop watchdog") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| self.quit = True | ||
| self.join() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| def dump_thread(self, stack): | ||
| for filename, lineno, name, line in traceback.extract_stack(stack): | ||
| self.write('File: "%s", line %d, in %s' % (filename, lineno, name)) | ||
| line = line.strip() | ||
| if line: | ||
| self.write(" %s" % line) | ||
|
|
||
| def dump_threads(self): | ||
| self.write("*** STACKTRACE - START ***") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the case if the previous outputted line is not ended with a newline and for attracting an attention add a |
||
|
|
||
| for threadId, stack in sys._current_frames().items(): | ||
| self.write("# ThreadID: %s" % threadId) | ||
| try: | ||
| self.dump_thread(stack) | ||
| except: | ||
| self.write("FAILED TO DUMP THREAD STACK") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Print a traceback? |
||
| self.write("") | ||
|
|
||
| self.write("*** STACKTRACE - END ***") | ||
|
|
||
|
|
||
| def main_in_temp_cwd(): | ||
| """Run main() in a temporary working directory.""" | ||
| global TEMPDIR | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why twice? Maybe sleep between them?