Content-Length: 6631 | pFad | http://github.com/python/cpython/pull/136770.diff
thub.com
diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index e8e2c1ed6047bf..0e749342426fd5 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -200,6 +200,11 @@ Other language changes
* Several error messages incorrectly using the term "argument" have been corrected.
(Contributed by Stan Ulbrych in :gh:`133382`.)
+* The :meth:`~object.__repr__` of :class:`ImportError` and :class:`ModuleNotFoundError`
+ now shows "name" and "path" as ``name=`` and ``path=`` if they were given
+ as keyword arguments at construction time.
+ (Contributed by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir in :gh:`74185`.)
+
New modules
===========
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index 57d0656487d4db..59f77f91d85e5c 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -2079,6 +2079,50 @@ def test_copy_pickle(self):
self.assertEqual(exc.name, orig.name)
self.assertEqual(exc.path, orig.path)
+ def test_repr(self):
+ exc = ImportError()
+ self.assertEqual(repr(exc), "ImportError()")
+
+ exc = ImportError('test')
+ self.assertEqual(repr(exc), "ImportError('test')")
+
+ exc = ImportError('test', 'case')
+ self.assertEqual(repr(exc), "ImportError('test', 'case')")
+
+ exc = ImportError(name='somemodule')
+ self.assertEqual(repr(exc), "ImportError(name='somemodule')")
+
+ exc = ImportError('test', name='somemodule')
+ self.assertEqual(repr(exc), "ImportError('test', name='somemodule')")
+
+ exc = ImportError(path='somepath')
+ self.assertEqual(repr(exc), "ImportError(path='somepath')")
+
+ exc = ImportError('test', path='somepath')
+ self.assertEqual(repr(exc), "ImportError('test', path='somepath')")
+
+ exc = ImportError(name='somename', path='somepath')
+ self.assertEqual(repr(exc),
+ "ImportError(name='somename', path='somepath')")
+
+ exc = ImportError('test', name='somename', path='somepath')
+ self.assertEqual(repr(exc),
+ "ImportError('test', name='somename', path='somepath')")
+
+ exc = ModuleNotFoundError('test', name='somename', path='somepath')
+ self.assertEqual(repr(exc),
+ "ModuleNotFoundError('test', name='somename', path='somepath')")
+
+ def test_ModuleNotFoundError_repr_with_failed_import(self):
+ with self.assertRaises(ModuleNotFoundError) as cm:
+ import does_not_exist # type: ignore[import] # noqa: F401
+
+ self.assertEqual(cm.exception.name, "does_not_exist")
+ self.assertIsNone(cm.exception.path)
+
+ self.assertEqual(repr(cm.exception),
+ "ModuleNotFoundError(\"No module named 'does_not_exist'\", name='does_not_exist')")
+
def run_script(source):
if isinstance(source, str):
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst
new file mode 100644
index 00000000000000..d149e7b2878574
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst
@@ -0,0 +1,4 @@
+The :meth:`~object.__repr__` of :class:`ImportError` and :class:`ModuleNotFoundError`
+now shows "name" and "path" as ``name=`` and ``path=`` if they were given
+as keyword arguments at construction time.
+Patch by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
index b17cac83551670..c41a8a0b37037f 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -1864,6 +1864,59 @@ ImportError_reduce(PyObject *self, PyObject *Py_UNUSED(ignored))
return res;
}
+static PyObject *
+ImportError_repr(PyObject *self)
+{
+ int hasargs = PyTuple_GET_SIZE(((PyBaseExceptionObject *)self)->args) != 0;
+ PyImportErrorObject *exc = PyImportErrorObject_CAST(self);
+ PyUnicodeWriter *writer = PyUnicodeWriter_Create(0);
+ if (writer == NULL) {
+ goto error;
+ }
+ PyObject *r = BaseException_repr(self);
+ if (r == NULL) {
+ goto error;
+ }
+ if (PyUnicodeWriter_WriteSubstring(
+ writer, r, 0, PyUnicode_GET_LENGTH(r) - 1) < 0)
+ {
+ Py_XDECREF(r);
+ goto error;
+ }
+ Py_XDECREF(r);
+ if (exc->name) {
+ if (hasargs) {
+ if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) {
+ goto error;
+ }
+ }
+ if (PyUnicodeWriter_Format(writer, "name=%R", exc->name) < 0) {
+ goto error;
+ }
+ hasargs = 1;
+ }
+ if (exc->path) {
+ if (hasargs) {
+ if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) {
+ goto error;
+ }
+ }
+ if (PyUnicodeWriter_Format(writer, "path=%R", exc->path) < 0) {
+ goto error;
+ }
+ }
+
+ if (PyUnicodeWriter_WriteChar(writer, ')') < 0) {
+ goto error;
+ }
+
+ return PyUnicodeWriter_Finish(writer);
+
+error:
+ PyUnicodeWriter_Discard(writer);
+ return NULL;
+}
+
static PyMemberDef ImportError_members[] = {
{"msg", _Py_T_OBJECT, offsetof(PyImportErrorObject, msg), 0,
PyDoc_STR("exception message")},
@@ -1881,12 +1934,26 @@ static PyMethodDef ImportError_methods[] = {
{NULL}
};
-ComplexExtendsException(PyExc_Exception, ImportError,
- ImportError, 0 /* new */,
- ImportError_methods, ImportError_members,
- 0 /* getset */, ImportError_str,
- "Import can't find module, or can't find name in "
- "module.");
+static PyTypeObject _PyExc_ImportError = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "ImportError",
+ .tp_basicsize = sizeof(PyImportErrorObject),
+ .tp_dealloc = ImportError_dealloc,
+ .tp_repr = ImportError_repr,
+ .tp_str = ImportError_str,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
+ .tp_doc = PyDoc_STR(
+ "Import can't find module, "
+ "or can't find name in module."),
+ .tp_traverse = ImportError_traverse,
+ .tp_clear = ImportError_clear,
+ .tp_methods = ImportError_methods,
+ .tp_members = ImportError_members,
+ .tp_base = &_PyExc_Exception,
+ .tp_dictoffset = offsetof(PyImportErrorObject, dict),
+ .tp_init = ImportError_init,
+};
+PyObject *PyExc_ImportError = (PyObject *)&_PyExc_ImportError;
/*
* ModuleNotFoundError extends ImportError
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/python/cpython/pull/136770.diff
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy