[Python-checkins] cpython: Issue #1559549: Add 'name' and 'path' attributes to ImportError.

brett.cannon python-checkins at python.org
Fri Apr 13 02:25:13 CEST 2012


http://hg.python.org/cpython/rev/6825fd9b00ed
changeset:   76272:6825fd9b00ed
user:        Brett Cannon <brett at python.org>
date:        Thu Apr 12 20:24:54 2012 -0400
summary:
  Issue #1559549: Add 'name' and 'path' attributes to ImportError.
Currently import does not use these attributes as they are planned
for use by importlib (which will be another commit).

Thanks to Filip Gruszczyński for the initial patch and Brian Curtin
for refining it.

files:
  Doc/library/exceptions.rst  |   8 ++
  Include/pyerrors.h          |  13 +++
  Lib/test/test_exceptions.py |  24 +++++-
  Misc/NEWS                   |   4 +
  Objects/exceptions.c        |  99 ++++++++++++++++++++++++-
  Python/errors.c             |  47 +++++++++++
  6 files changed, 192 insertions(+), 3 deletions(-)


diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst
--- a/Doc/library/exceptions.rst
+++ b/Doc/library/exceptions.rst
@@ -159,6 +159,14 @@
    Raised when an :keyword:`import` statement fails to find the module definition
    or when a ``from ... import`` fails to find a name that is to be imported.
 
+   The :attr:`name` and :attr:`path` attributes can be set using keyword-only
+   arguments to the constructor. When set they represent the name of the module
+   that was attempted to be imported and the path to any file which triggered
+   the exception, respectively.
+
+   .. versionchanged:: 3.3
+      Added the :attr:`name` and :attr:`path` attributes.
+
 
 .. exception:: IndexError
 
diff --git a/Include/pyerrors.h b/Include/pyerrors.h
--- a/Include/pyerrors.h
+++ b/Include/pyerrors.h
@@ -231,6 +231,13 @@
     ...
     );
 
+typedef struct {
+    PyException_HEAD
+    PyObject *msg;
+    PyObject *name;
+    PyObject *path;
+} PyImportErrorObject;
+
 #ifdef MS_WINDOWS
 PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilename(
     int ierr,
@@ -256,6 +263,12 @@
 PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErr(PyObject *, int);
 #endif /* MS_WINDOWS */
 
+PyAPI_FUNC(PyObject *) PyErr_SetExcWithArgsKwargs(PyObject *, PyObject *,
+    PyObject *);
+PyAPI_FUNC(PyObject *) PyErr_SetFromImportErrorWithNameAndPath(PyObject *,
+        PyObject *, PyObject *);
+PyAPI_FUNC(PyObject *) PyErr_SetFromImportErrorWithName(PyObject *, PyObject *);
+
 /* Export the old function so that the existing API remains available: */
 PyAPI_FUNC(void) PyErr_BadInternalCall(void);
 PyAPI_FUNC(void) _PyErr_BadInternalCall(const char *filename, int lineno);
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -902,8 +902,30 @@
         self.assertEqual(cm.exception.errno, errno.ENOTDIR, cm.exception)
 
 
+class ImportErrorTests(unittest.TestCase):
+
+    def test_attributes(self):
+        # Setting 'name' and 'path' should not be a problem.
+        exc = ImportError('test')
+        self.assertIsNone(exc.name)
+        self.assertIsNone(exc.path)
+
+        exc = ImportError('test', name='somemodule')
+        self.assertEqual(exc.name, 'somemodule')
+        self.assertIsNone(exc.path)
+
+        exc = ImportError('test', path='somepath')
+        self.assertEqual(exc.path, 'somepath')
+        self.assertIsNone(exc.name)
+
+        exc = ImportError('test', path='somepath', name='somename')
+        self.assertEqual(exc.name, 'somename')
+        self.assertEqual(exc.path, 'somepath')
+
+
+
 def test_main():
-    run_unittest(ExceptionTests)
+    run_unittest(ExceptionTests, ImportErrorTests)
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,10 @@
 Core and Builtins
 -----------------
 
+- Issue #1559549: ImportError now has 'name' and 'path' attributes that are set
+  using keyword arguments to its constructor. They are currently not set by
+  import as they are meant for use by importlib.
+
 - Issue #14474: Save and restore exception state in thread.start_new_thread()
   while writing error message if the thread leaves a unhandled exception.
 
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -605,9 +605,104 @@
 /*
  *    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) { \
+    kwd = PyDict_GetItemString(kwds, #kwd); \
+    if (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)
 {

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list