diff --git a/Include/pyerrors.h b/Include/pyerrors.h --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -230,6 +230,13 @@ ... ); +typedef struct { + PyException_HEAD + PyObject *msg; + PyObject *name; + PyObject *path; +} PyImportErrorObject; + #ifdef MS_WINDOWS PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilename( int ierr, diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py --- a/Lib/test/test_import.py +++ b/Lib/test/test_import.py @@ -306,6 +306,20 @@ sys.argv.insert(0, C()) """)) script_helper.assert_python_ok(testfn) + + def test_exception_keywords(self): + with self.assertRaises(ImportError) as exc1: + raise ImportError('test', name='name') + self.assertEqual(exc1.exception.name, 'name') + + with self.assertRaises(ImportError) as exc2: + raise ImportError('test', path='path') + self.assertEqual(exc2.exception.path, 'path') + + with self.assertRaises(ImportError) as exc3: + raise ImportError('test', name='name', path='path') + self.assertEqual(exc3.exception.name, 'name') + self.assertEqual(exc3.exception.path, 'path') def test_timestamp_overflow(self): # A modification timestamp larger than 2**32 should not be a problem diff --git a/Objects/exceptions.c b/Objects/exceptions.c --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -628,9 +628,101 @@ /* * ImportError extends Exception */ -SimpleExtendsException(PyExc_Exception, ImportError, - "Import can't find module, or can't find name in module."); +static int +ImportError_init(PyImportErrorObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *msg = NULL; + PyObject *name = NULL; + PyObject *path = NULL; + +/* Macro replacement doesn't allow ## to start the first line of a macro, + so we move the assignment and NULL check into the if-statement. */ +#define GET_KWD(kwd) \ +if ((##kwd = PyDict_GetItemString(kwds, #kwd)) && ##kwd) { \ + Py_CLEAR(self->##kwd); \ + self->##kwd = ##kwd; \ + Py_INCREF(self->##kwd);\ + if (PyDict_DelItemString(kwds, #kwd)) \ + return -1; \ +} + + if (kwds) { + GET_KWD(name); + GET_KWD(path); + } + + if (BaseException_init((PyBaseExceptionObject *)self, args, kwds) == -1) + return -1; + if (PyTuple_GET_SIZE(args) != 1) + return 0; + if (!PyArg_UnpackTuple(args, "ImportError", 1, 1, &msg)) + return -1; + + Py_CLEAR(self->msg); /* replacing */ + self->msg = msg; + Py_INCREF(self->msg); + + return 0; +} + +static int +ImportError_clear(PyImportErrorObject *self) +{ + Py_CLEAR(self->msg); + Py_CLEAR(self->name); + Py_CLEAR(self->path); + return BaseException_clear((PyBaseExceptionObject *)self); +} + +static void +ImportError_dealloc(PyImportErrorObject *self) +{ + _PyObject_GC_UNTRACK(self); + ImportError_clear(self); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static int +ImportError_traverse(PyImportErrorObject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->msg); + Py_VISIT(self->name); + Py_VISIT(self->path); + return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg); +} + +static PyObject * +ImportError_str(PyImportErrorObject *self) +{ + if (self->msg) { + Py_INCREF(self->msg); + return self->msg; + } + else + return BaseException_str((PyBaseExceptionObject *)self); +} + +static PyMemberDef ImportError_members[] = { + {"msg", T_OBJECT, offsetof(PyImportErrorObject, msg), 0, + PyDoc_STR("exception message")}, + {"name", T_OBJECT, offsetof(PyImportErrorObject, name), 0, + PyDoc_STR("module name")}, + {"path", T_OBJECT, offsetof(PyImportErrorObject, path), 0, + PyDoc_STR("module path")}, + {NULL} /* Sentinel */ +}; + +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."); /* * OSError extends Exception diff --git a/Python/errors.c b/Python/errors.c --- a/Python/errors.c +++ b/Python/errors.c @@ -585,6 +585,53 @@ } #endif /* MS_WINDOWS */ +PyObject * +PyErr_SetExcWithArgsKwargs(PyObject *exc, PyObject *args, PyObject *kwargs) +{ + PyObject *val; + + /* args must at least be an empty tuple */ + if (args == NULL) + args = PyTuple_New(0); + + val = PyObject_Call(exc, args, kwargs); + if (val != NULL) { + PyErr_SetObject((PyObject *) Py_TYPE(val), val); + Py_DECREF(val); + } + + return NULL; +} + +PyObject * +PyErr_SetFromImportErrorWithNameAndPath(PyObject *msg, + PyObject *name, PyObject *path) +{ + PyObject *args = PyTuple_New(1); + PyObject *kwargs = PyDict_New(); + PyObject *result; + + if (path == NULL) + path = Py_None; + + PyTuple_SetItem(args, 0, msg); + PyDict_SetItemString(kwargs, "name", name); + PyDict_SetItemString(kwargs, "path", path); + + result = PyErr_SetExcWithArgsKwargs(PyExc_ImportError, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + + return result; +} + +PyObject * +PyErr_SetFromImportErrorWithName(PyObject *msg, PyObject *name) +{ + return PyErr_SetFromImportErrorWithNameAndPath(msg, name, NULL); +} + void _PyErr_BadInternalCall(const char *filename, int lineno) { diff --git a/Python/import.c b/Python/import.c --- a/Python/import.c +++ b/Python/import.c @@ -871,9 +871,8 @@ Py_DECREF(v); if ((m = PyDict_GetItem(modules, name)) == NULL) { - PyErr_Format(PyExc_ImportError, - "Loaded module %R not found in sys.modules", - name); + PyErr_SetFromImportErrorWithName(PyUnicode_FromFormat( + "Loaded module %R not found in sys.modules", name), name); return NULL; } @@ -1120,8 +1119,8 @@ if (co == NULL) return NULL; if (!PyCode_Check(co)) { - PyErr_Format(PyExc_ImportError, - "Non-code object in %R", cpathname); + PyErr_SetFromImportErrorWithName(PyUnicode_FromFormat( + "Non-code object in %R", cpathname), cpathname); Py_DECREF(co); return NULL; } @@ -1141,8 +1140,8 @@ magic = PyMarshal_ReadLongFromFile(fp); if (magic != pyc_magic) { - PyErr_Format(PyExc_ImportError, - "Bad magic number in %R", cpathname); + PyErr_SetFromImportErrorWithName(PyUnicode_FromFormat( + "Bad magic number in %R", cpathname), cpathname); return NULL; } /* Skip mtime and size */ @@ -2005,8 +2004,8 @@ } Py_DECREF(prefix); } - PyErr_Format(PyExc_ImportError, - "No module named %R", name); + PyErr_SetFromImportErrorWithName( + PyUnicode_FromFormat("No module named %R", name), name); return NULL; } @@ -2430,21 +2429,20 @@ if (err < 0) return NULL; if (err == 0) { - PyErr_Format(PyExc_ImportError, + PyErr_SetFromImportErrorWithName(PyUnicode_FromFormat( "Purported %s module %R not found", type == C_BUILTIN ? "builtin" : "frozen", - name); + name), name); return NULL; } modules = PyImport_GetModuleDict(); m = PyDict_GetItem(modules, name); if (m == NULL) { - PyErr_Format( - PyExc_ImportError, + PyErr_SetFromImportErrorWithName(PyUnicode_FromFormat( "%s module %R not properly initialized", type == C_BUILTIN ? "builtin" : "frozen", - name); + name), name); return NULL; } Py_INCREF(m); @@ -2508,9 +2506,9 @@ } default: - PyErr_Format(PyExc_ImportError, + PyErr_SetFromImportErrorWithName(PyUnicode_FromFormat( "Don't know how to import %R (type code %d)", - name, type); + name, type), name); m = NULL; } @@ -2536,9 +2534,9 @@ PyModuleDef *def; if (PyUnicode_CompareWithASCIIString(name, p->name) == 0) { if (p->initfunc == NULL) { - PyErr_Format(PyExc_ImportError, + PyErr_SetFromImportErrorWithName(PyUnicode_FromFormat( "Cannot re-init internal module %R", - name); + name), name); return -1; } if (Py_VerboseFlag) @@ -2587,15 +2585,15 @@ int size; if (p == NULL) { - PyErr_Format(PyExc_ImportError, + PyErr_SetFromImportErrorWithName(PyUnicode_FromFormat( "No such frozen object named %R", - name); + name), name); return NULL; } if (p->code == NULL) { - PyErr_Format(PyExc_ImportError, + PyErr_SetFromImportErrorWithName(PyUnicode_FromFormat( "Excluded frozen object named %R", - name); + name), name); return NULL; } size = p->size; @@ -2611,9 +2609,9 @@ int size; if (p == NULL) { - PyErr_Format(PyExc_ImportError, + PyErr_SetFromImportErrorWithName(PyUnicode_FromFormat( "No such frozen object named %R", - name); + name), name); return NULL; } @@ -2644,9 +2642,9 @@ if (p == NULL) return 0; if (p->code == NULL) { - PyErr_Format(PyExc_ImportError, + PyErr_SetFromImportErrorWithName(PyUnicode_FromFormat( "Excluded frozen object named %R", - name); + name), name); return -1; } size = p->size; @@ -2772,10 +2770,10 @@ result = PyImport_Import(nameobj); } else { - PyErr_Format(PyExc_ImportError, + PyErr_SetFromImportErrorWithName(PyUnicode_FromFormat( "Failed to import %R because the import lock" "is held by another thread.", - nameobj); + nameobj), nameobj); result = NULL; } #else @@ -3169,8 +3167,8 @@ if (result == Py_None) { Py_DECREF(result); - PyErr_Format(PyExc_ImportError, - "No module named %R", inputname); + PyErr_SetFromImportErrorWithName( + PyUnicode_FromFormat("No module named %R", inputname), inputname); goto error; } @@ -3381,9 +3379,9 @@ if (name == NULL || PyUnicode_READY(name) == -1) return NULL; if (m != PyDict_GetItem(modules, name)) { - PyErr_Format(PyExc_ImportError, - "reload(): module %R not in sys.modules", - name); + PyErr_SetFromImportErrorWithName( + PyUnicode_FromFormat("reload(): module %R not in sys.modules", + name), name); Py_DECREF(name); return NULL; } @@ -3415,9 +3413,9 @@ } parent = PyDict_GetItem(modules, parentname); if (parent == NULL) { - PyErr_Format(PyExc_ImportError, + PyErr_SetFromImportErrorWithName(PyUnicode_FromFormat( "reload(): parent %R not in sys.modules", - parentname); + parentname), parentname); Py_DECREF(parentname); goto error; }