From e8c8dc392d067cb0f4df998b27b69427c7eea09a 2021-04-14 23:48:45 From: Matthias Bussonnier Date: 2021-04-14 23:48:45 Subject: [PATCH] Backport PR #12758: Fix issue #11615 handle unicode encode error when print stack trace --- diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 6ab436f..cb0870e 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2075,13 +2075,17 @@ class InteractiveShell(SingletonConfigurable): except KeyboardInterrupt: print('\n' + self.get_exception_only(), file=sys.stderr) - def _showtraceback(self, etype, evalue, stb): + def _showtraceback(self, etype, evalue, stb: str): """Actually show a traceback. Subclasses may override this method to put the traceback on a different place, like a side channel. """ - print(self.InteractiveTB.stb2text(stb)) + val = self.InteractiveTB.stb2text(stb) + try: + print(val) + except UnicodeEncodeError: + print(val.encode("utf-8", "backslashreplace").decode()) def showsyntaxerror(self, filename=None, running_compiled_code=False): """Display the syntax error that just occurred. diff --git a/IPython/core/tests/test_interactiveshell.py b/IPython/core/tests/test_interactiveshell.py index 951b843..84bea9c 100644 --- a/IPython/core/tests/test_interactiveshell.py +++ b/IPython/core/tests/test_interactiveshell.py @@ -449,6 +449,25 @@ class InteractiveShellTestCase(unittest.TestCase): # Reset the custom exception hook ip.set_custom_exc((), None) + @mock.patch("builtins.print") + def test_showtraceback_with_surrogates(self, mocked_print): + values = [] + + def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False): + values.append(value) + if value == chr(0xD8FF): + raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "") + + # mock builtins.print + mocked_print.side_effect = mock_print_func + + # ip._showtraceback() is replaced in globalipapp.py. + # Call original method to test. + interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF)) + + self.assertEqual(mocked_print.call_count, 2) + self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"]) + def test_mktempfile(self): filename = ip.mktempfile() # Check that we can open the file again on Windows