diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -48,20 +48,11 @@ support.unlink(filename) class FaultHandlerTests(unittest.TestCase): - def get_output(self, code, filename=None): - """ - Run the specified code in Python (in a new child process) and read the - output from the standard error or from a file (if filename is set). - Return the output lines as a list. - - Strip the reference count from the standard error for Python debug - build, and replace "Current thread 0x00007f8d8fbd9700" by "Current - thread XXX". - """ + def _get_output_args(self, python_args, filename=None): options = {} if prepare_subprocess: options['preexec_fn'] = prepare_subprocess - process = script_helper.spawn_python('-c', code, **options) + process = script_helper.spawn_python(*python_args, **options) stdout, stderr = process.communicate() exitcode = process.wait() output = support.strip_python_stderr(stdout) @@ -76,6 +67,33 @@ output) return output.splitlines(), exitcode + def _get_output_code(self, code, filename=None): + return self._get_output_args(['-c', code], filename) + + def _get_output_script(self, code, scriptpath, filename=None): + with script_helper.temp_dir() as tempdir: + scriptpath = os.path.join(tempdir, scriptpath) + scriptdir, scriptbase = os.path.split(scriptpath) + os.makedirs(scriptdir) + scriptname, ext = os.path.splitext(scriptbase) + scriptpath = script_helper.make_script(scriptdir, scriptname, code) + return self._get_output_args([scriptpath], filename) + + def get_output(self, code, filename=None, scriptpath=None): + """ + Run the specified code in Python (in a new child process) and read the + output from the standard error or from a file (if filename is set). + Return the output lines as a list. + + Strip the reference count from the standard error for Python debug + build, and replace "Current thread 0x00007f8d8fbd9700" by "Current + thread XXX". + """ + if scriptpath: + return self._get_output_script(code, scriptpath, filename) + else: + return self._get_output_code(code, filename) + def check_fatal_error(self, code, line_number, name_regex, filename=None, all_threads=True, other_regex=None): """ @@ -256,7 +274,7 @@ finally: sys.stderr = orig_stderr - def check_dump_traceback(self, filename): + def check_dump_traceback(self, filename, scriptpath=None): """ Explicitly call dump_traceback() function and check its output. Raise an error if the output doesn't match the expected format. @@ -284,14 +302,21 @@ lineno = 6 else: lineno = 8 - expected = [ - 'Traceback (most recent call first):', - ' File "", line %s in funcB' % lineno, - ' File "", line 11 in funcA', - ' File "", line 13 in ' - ] - trace, exitcode = self.get_output(code, filename) - self.assertEqual(trace, expected) + if scriptpath: + script = ".*%s" % os.path.basename(scriptpath) + else: + script = "" + regex = """ +^Traceback \(most recent call first\): + File "{script}", line {lineno} in funcB + File "{script}", line 11 in funcA + File "{script}", line 13 in $ +""".strip() + regex = regex.format(lineno=lineno, script=script) + trace, exitcode = self.get_output(code, filename, + scriptpath=scriptpath) + trace = '\n'.join(trace) + self.assertRegex(trace, regex) self.assertEqual(exitcode, 0) def test_dump_traceback(self): @@ -301,6 +326,16 @@ with temporary_filename() as filename: self.check_dump_traceback(filename) + def test_dump_traceback_long_script_path(self): + """ + Test a traceback with a script path longer than 100 characters. + + See issue #15463. + """ + names = 10 * [10 * 'a'] + scriptpath = os.path.join(*names) + ".py" + self.check_dump_traceback(None, scriptpath=scriptpath) + @unittest.skipIf(not HAVE_THREADS, 'need threads') def check_dump_traceback_threads(self, filename): """