mirror of
https://github.com/python/cpython.git
synced 2025-10-25 10:44:55 +00:00
gh-74185: repr() of ImportError now contains attributes name and path (#136770)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Oleg Iarygin <oleg@arhadthedev.net> Co-authored-by: ynir3 <ynir3@bloomberg.net>
This commit is contained in:
parent
c47ffbf1a3
commit
c87b66bc7c
4 changed files with 128 additions and 6 deletions
|
|
@ -204,6 +204,10 @@ Other language changes
|
||||||
controlled by :ref:`environment variables <using-on-controlling-color>`.
|
controlled by :ref:`environment variables <using-on-controlling-color>`.
|
||||||
(Contributed by Peter Bierma in :gh:`134170`.)
|
(Contributed by Peter Bierma in :gh:`134170`.)
|
||||||
|
|
||||||
|
* The :meth:`~object.__repr__` of :class:`ImportError` and :class:`ModuleNotFoundError`
|
||||||
|
now shows "name" and "path" as ``name=<name>`` and ``path=<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
|
New modules
|
||||||
===========
|
===========
|
||||||
|
|
|
||||||
|
|
@ -2079,6 +2079,50 @@ def test_copy_pickle(self):
|
||||||
self.assertEqual(exc.name, orig.name)
|
self.assertEqual(exc.name, orig.name)
|
||||||
self.assertEqual(exc.path, orig.path)
|
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):
|
def run_script(source):
|
||||||
if isinstance(source, str):
|
if isinstance(source, str):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
The :meth:`~object.__repr__` of :class:`ImportError` and :class:`ModuleNotFoundError`
|
||||||
|
now shows "name" and "path" as ``name=<name>`` and ``path=<path>`` if they were given
|
||||||
|
as keyword arguments at construction time.
|
||||||
|
Patch by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir
|
||||||
|
|
@ -1864,6 +1864,62 @@ ImportError_reduce(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
ImportError_repr(PyObject *self)
|
||||||
|
{
|
||||||
|
int hasargs = PyTuple_GET_SIZE(((PyBaseExceptionObject *)self)->args) != 0;
|
||||||
|
PyImportErrorObject *exc = PyImportErrorObject_CAST(self);
|
||||||
|
if (exc->name == NULL && exc->path == NULL) {
|
||||||
|
return BaseException_repr(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_DECREF(r);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
Py_DECREF(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[] = {
|
static PyMemberDef ImportError_members[] = {
|
||||||
{"msg", _Py_T_OBJECT, offsetof(PyImportErrorObject, msg), 0,
|
{"msg", _Py_T_OBJECT, offsetof(PyImportErrorObject, msg), 0,
|
||||||
PyDoc_STR("exception message")},
|
PyDoc_STR("exception message")},
|
||||||
|
|
@ -1881,12 +1937,26 @@ static PyMethodDef ImportError_methods[] = {
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
ComplexExtendsException(PyExc_Exception, ImportError,
|
static PyTypeObject _PyExc_ImportError = {
|
||||||
ImportError, 0 /* new */,
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
ImportError_methods, ImportError_members,
|
.tp_name = "ImportError",
|
||||||
0 /* getset */, ImportError_str,
|
.tp_basicsize = sizeof(PyImportErrorObject),
|
||||||
"Import can't find module, or can't find name in "
|
.tp_dealloc = ImportError_dealloc,
|
||||||
"module.");
|
.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
|
* ModuleNotFoundError extends ImportError
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue