From 96ceaf319f8b96050f4d0f8b8edaa849baa2dca6 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Thu, 20 Aug 2026 19:26:22 +0000 Subject: [PATCH 1/3] gh-156126: Fix crash in -X importtime with unencodable module names --- Lib/test/test_cmd_line.py | 18 +++++++++++++++ ...-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst | 3 +++ Python/import.c | 23 +++++++++++++++++-- 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index 25d6d1a248b4577..555cebeadc60538 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -1267,6 +1267,24 @@ def test_import_time(self): assert_python_failure('-X', 'importtime=-1', '-c', code) assert_python_failure('-X', 'importtime=3', '-c', code) + def test_import_time_unencodable_module_name(self): + code = textwrap.dedent(""" + import sys, types + name = 'mod\\ud800' + sys.modules[name] = types.ModuleType(name) + __import__(name) + try: + __import__('nonexistent\\ud800') + except ModuleNotFoundError: + pass + """) + res = assert_python_ok('-X', 'importtime=2', '-c', code) + res_err = res.err.decode('utf-8') + self.assertRegex(res_err, + r'import time: cached\s* \| cached\s* \| mod\\ud800') + self.assertRegex(res_err, + r'import time: \s*\d+ \| \s*\d+ \| \s*nonexistent\\ud800') + def res2int(self, res): out = res.out.strip().decode("utf-8") return tuple(int(i) for i in out.split()) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst new file mode 100644 index 000000000000000..1c1d905dfb8f247 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst @@ -0,0 +1,3 @@ +Fix a crash when importing a module whose name contains characters that +cannot be encoded to UTF-8 (such as lone surrogates) while :option:`-X +importtime=2>` is enabled. diff --git a/Python/import.c b/Python/import.c index 47d5296a2fec9b1..037f15d4ca2bafa 100644 --- a/Python/import.c +++ b/Python/import.c @@ -286,6 +286,19 @@ _PyImport_ClearLazyModules(PyInterpreterState *interp) Py_CLEAR(LAZY_PENDING_SUBMODULES(interp)); } +static PyObject * +get_importtime_name(PyObject *name) +{ + PyObject *exc = PyErr_GetRaisedException(); + PyObject *encoded = PyUnicode_AsEncodedString(name, "utf-8", + "backslashreplace"); + if (encoded == NULL) { + PyErr_Clear(); + } + PyErr_SetRaisedException(exc); + return encoded; +} + static int import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *name) { @@ -323,8 +336,11 @@ import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *n if (_PyInterpreterState_GetConfig(interp)->import_time == 2) { _IMPORT_TIME_HEADER(interp); #define import_level FIND_AND_LOAD(interp).import_level + PyObject *encoded_name = get_importtime_name(name); fprintf(stderr, "import time: cached | cached | %*s\n", - import_level*2, PyUnicode_AsUTF8(name)); + import_level*2, + encoded_name != NULL ? PyBytes_AS_STRING(encoded_name) : "?"); + Py_XDECREF(encoded_name); #undef import_level } @@ -4121,10 +4137,13 @@ import_find_and_load_with_name(PyThreadState *tstate, PyObject *abs_name, PyTime_t cum = t2 - t1; import_level--; + PyObject *encoded_name = get_importtime_name(abs_name); fprintf(stderr, "import time: %9ld | %10ld | %*s%s\n", (long)_PyTime_AsMicroseconds(cum - accumulated, _PyTime_ROUND_CEILING), (long)_PyTime_AsMicroseconds(cum, _PyTime_ROUND_CEILING), - import_level*2, "", PyUnicode_AsUTF8(abs_name)); + import_level*2, "", + encoded_name != NULL ? PyBytes_AS_STRING(encoded_name) : "?"); + Py_XDECREF(encoded_name); accumulated = accumulated_copy + cum; } From 0e2febf0b77b0ef30d2f0941c3af5e1d5075517b Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Thu, 20 Aug 2026 19:29:54 +0000 Subject: [PATCH 2/3] fix news --- .../2026-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst index 1c1d905dfb8f247..783257e685af1e1 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst @@ -1,3 +1,3 @@ Fix a crash when importing a module whose name contains characters that cannot be encoded to UTF-8 (such as lone surrogates) while :option:`-X -importtime=2>` is enabled. +importtime <- X>` is enabled. From 737c9e4103f13186a44c793d5dd0d4e33b5c72f0 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Thu, 20 Aug 2026 19:42:23 +0000 Subject: [PATCH 3/3] hope that'll help --- .../2026-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst index 783257e685af1e1..d8cfefb86b06dd2 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst @@ -1,3 +1,3 @@ Fix a crash when importing a module whose name contains characters that cannot be encoded to UTF-8 (such as lone surrogates) while :option:`-X -importtime <- X>` is enabled. +importtime <-X>` is enabled.