[3.14] gh-156126: Fix crash in -X importtime with unencodable module … (#156330)

Eclips4 <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/7267c9b8f4973bf5be785ebf3ddb9624ed5d4446
commit: 7267c9b8f4973bf5be785ebf3ddb9624ed5d4446
branch: 3.14
author: Kirill Podoprigora <[email protected]>
committer: Eclips4 <[email protected]>
date: 2026-08-25T00:11:08+03:00
summary:

[3.14] gh-156126: Fix crash in -X importtime with unencodable module … (#156330)

[3.14] gh-156126: Fix crash in -X importtime with unencodable module names (GH-156137)

(cherry picked from commit 3a5aa685885d8a2103f7d8de17481be64bc84dfb)

files:
A Misc/NEWS.d/next/Core_and_Builtins/2026-08-20-22-13-20.gh-issue-156126.pXm4Qr.rst
M Lib/test/test_cmd_line.py
M Python/import.c

diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py
index adc665135576d46..e101d54cf04171f 100644
--- a/Lib/test/test_cmd_line.py
+++ b/Lib/test/test_cmd_line.py
@@ -1225,6 +1225,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..d8cfefb86b06dd2
--- /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 <-X>` is enabled.
diff --git a/Python/import.c b/Python/import.c
index d18ca3d87e09ebd..fded9a3e849f67c 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -241,6 +241,19 @@ import_get_module(PyThreadState *tstate, PyObject *name)
     return m;
 }
 
+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)
 {
@@ -278,8 +291,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
     }
 
@@ -3782,10 +3798,13 @@ import_find_and_load(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;
     }

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.