From 0e41d545e70be7a3f7338f6543d1d62a554268b6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 25 May 2022 12:08:01 +0200 Subject: [PATCH 1/2] gh-91924: Fix __ltrace__ for non-UTF-8 stdout encoding 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 | 66 ++++++++++++++++++- ...2-05-25-04-07-22.gh-issue-91924.-UyO4q.rst | 2 + Python/ceval.c | 2 + 3 files changed, 69 insertions(+), 1 deletion(-) 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 06e33f4c4c2f38..7ff6800ed96845 100644 --- a/Lib/test/test_lltrace.py +++ b/Lib/test/test_lltrace.py @@ -1,8 +1,10 @@ +import opcode import os +import re 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 @@ -27,5 +29,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 00000000000000..3986ad8aa8d951 --- /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 21674e0be13240..9a193c994d12ec 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 From 9fc23fd690cc7022bbb531c69613290f5d4bcf81 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 25 May 2022 21:21:08 +0200 Subject: [PATCH 2/2] Skip test if Python is built in release mode --- Lib/test/test_lltrace.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_lltrace.py b/Lib/test/test_lltrace.py index 7ff6800ed96845..b5b0c10d404be9 100644 --- a/Lib/test/test_lltrace.py +++ b/Lib/test/test_lltrace.py @@ -1,6 +1,6 @@ import opcode -import os import re +import sys import textwrap import unittest @@ -8,6 +8,9 @@ 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):