From 9369942054fe3fe389f4f4ff808d33c5d7945052 Mon Sep 17 00:00:00 2001 From: Victor Stinner <vstinner@python.org> Date: Thu, 26 May 2022 00:16:32 +0200 Subject: [PATCH] [3.10] gh-91924: Fix __ltrace__ for non-UTF-8 stdout encoding (#93214) Fix __ltrace__ debug feature if the stdout encoding is not UTF-8. If the stdout encoding is not UTF-8, the first call to lltrace_resume_frame() indirectly sets lltrace to 0 when calling unicode_check_encoding_errors() which calls encodings.search_function(). Add test_lltrace.test_lltrace() test. --- Lib/test/test_lltrace.py | 71 ++++++++++++++++++- ...2-05-25-04-07-22.gh-issue-91924.-UyO4q.rst | 2 + Python/ceval.c | 2 + 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-05-25-04-07-22.gh-issue-91924.-UyO4q.rst diff --git a/Lib/test/test_lltrace.py b/Lib/test/test_lltrace.py index 06e33f4c4c2..b5b0c10d404 100644 --- a/Lib/test/test_lltrace.py +++ b/Lib/test/test_lltrace.py @@ -1,11 +1,16 @@ -import os +import opcode +import re +import sys import textwrap import unittest -from test.support import os_helper +from test.support import os_helper, verbose from test.support.script_helper import assert_python_ok +Py_DEBUG = hasattr(sys, 'gettotalrefcount') + +@unittest.skipUnless(Py_DEBUG, "lltrace requires Py_DEBUG") class TestLLTrace(unittest.TestCase): def test_lltrace_does_not_crash_on_subscript_operator(self): @@ -27,5 +32,67 @@ def test_lltrace_does_not_crash_on_subscript_operator(self): assert_python_ok(os_helper.TESTFN) + def run_code(self, code): + code = textwrap.dedent(code).strip() + with open(os_helper.TESTFN, 'w', encoding='utf-8') as fd: + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + fd.write(code) + status, stdout, stderr = assert_python_ok(os_helper.TESTFN) + self.assertEqual(stderr, b"") + self.assertEqual(status, 0) + result = stdout.decode('utf-8') + if verbose: + print("\n\n--- code ---") + print(code) + print("\n--- stdout ---") + print(result) + print() + return result + + def check_op(self, op, stdout, present): + op = opcode.opmap[op] + regex = re.compile(f': {op}($|, )', re.MULTILINE) + if present: + self.assertTrue(regex.search(stdout), + f'": {op}" not found in: {stdout}') + else: + self.assertFalse(regex.search(stdout), + f'": {op}" found in: {stdout}') + + def check_op_in(self, op, stdout): + self.check_op(op, stdout, True) + + def check_op_not_in(self, op, stdout): + self.check_op(op, stdout, False) + + def test_lltrace(self): + stdout = self.run_code(""" + def dont_trace_1(): + a = "a" + a = 10 * a + def trace_me(): + for i in range(3): + +i + def dont_trace_2(): + x = 42 + y = -x + dont_trace_1() + __ltrace__ = 1 + trace_me() + del __ltrace__ + dont_trace_2() + """) + self.check_op_in("GET_ITER", stdout) + self.check_op_in("FOR_ITER", stdout) + self.check_op_in("UNARY_POSITIVE", stdout) + self.check_op_in("POP_TOP", stdout) + + # before: dont_trace_1() is not traced + self.check_op_not_in("BINARY_MULTIPLY", stdout) + + # after: dont_trace_2() is not traced + self.check_op_not_in("UNARY_NEGATIVE", stdout) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-05-25-04-07-22.gh-issue-91924.-UyO4q.rst b/Misc/NEWS.d/next/Core and Builtins/2022-05-25-04-07-22.gh-issue-91924.-UyO4q.rst new file mode 100644 index 00000000000..3986ad8aa8d --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-05-25-04-07-22.gh-issue-91924.-UyO4q.rst @@ -0,0 +1,2 @@ +Fix ``__ltrace__`` debug feature if the stdout encoding is not UTF-8. Patch +by Victor Stinner. diff --git a/Python/ceval.c b/Python/ceval.c index 21674e0be13..9a193c994d1 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5377,6 +5377,8 @@ prtrace(PyThreadState *tstate, PyObject *v, const char *str) } printf("\n"); PyErr_Restore(type, value, traceback); + // gh-91924: PyObject_Print() can indirectly set lltrace to 0 + lltrace = 1; return 1; } #endif -- GitLab