diff options
-rw-r--r-- | Doc/c-api/exceptions.rst | 7 | ||||
-rw-r--r-- | Doc/library/exceptions.rst | 3 | ||||
-rw-r--r-- | Doc/reference/import.rst | 12 | ||||
-rw-r--r-- | Doc/whatsnew/3.4.rst | 3 | ||||
-rw-r--r-- | Include/pyerrors.h | 3 | ||||
-rw-r--r-- | Lib/importlib/_bootstrap.py | 2 | ||||
-rw-r--r-- | Misc/NEWS | 4 | ||||
-rw-r--r-- | Python/errors.c | 25 | ||||
-rw-r--r-- | Python/import.c | 3 | ||||
-rw-r--r-- | Python/importlib.h | 4 |
10 files changed, 52 insertions, 14 deletions
diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 1bdcdd3..25e2a1c 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -292,6 +292,13 @@ in various ways. There is a separate error indicator for each thread. .. versionadded:: 3.3 +.. c:function:: PyObject* PyErr_SetImportErrorSubclass(PyObject *msg, PyObject *name, PyObject *path) + + Much like :c:func:`PyErr_SetImportError` but this function allows for + specifying a subclass of :exc:`ImportError` to raise. + + .. versionadded:: 3.4 + .. c:function:: void PyErr_SyntaxLocationEx(char *filename, int lineno, int col_offset) diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index 933667c..377d98a 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -185,7 +185,8 @@ The following exceptions are the exceptions that are usually raised. A subclass of :exc:`ImportError` which is raised by :keyword:`import` when a module could not be located. This includes ``from ... import`` statements as the specific attribute being requested cannot be known a priori to be a module - or some other type of object. + or some other type of object. It is also raised when ``None`` is found in + :data:`sys.modules`. .. versionadded:: 3.4 diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst index b587fc9..73f5ae5 100644 --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -37,7 +37,7 @@ use the standard import system. When a module is first imported, Python searches for the module and if found, it creates a module object [#fnmo]_, initializing it. If the named module -cannot be found, an :exc:`ImportError` is raised. Python implements various +cannot be found, an :exc:`ModuleNotFoundError` is raised. Python implements various strategies to search for the named module when the import machinery is invoked. These strategies can be modified and extended by using various hooks described in the sections below. @@ -168,7 +168,7 @@ arguments to the :keyword:`import` statement, or from the parameters to the This name will be used in various phases of the import search, and it may be the dotted path to a submodule, e.g. ``foo.bar.baz``. In this case, Python first tries to import ``foo``, then ``foo.bar``, and finally ``foo.bar.baz``. -If any of the intermediate imports fail, an :exc:`ImportError` is raised. +If any of the intermediate imports fail, an :exc:`ModuleNotFoundError` is raised. The module cache @@ -187,7 +187,7 @@ object. During import, the module name is looked up in :data:`sys.modules` and if present, the associated value is the module satisfying the import, and the process completes. However, if the value is ``None``, then an -:exc:`ImportError` is raised. If the module name is missing, Python will +:exc:`ModuleNotFoundError` is raised. If the module name is missing, Python will continue searching for the module. :data:`sys.modules` is writable. Deleting a key may not destroy the @@ -195,7 +195,7 @@ associated module (as other modules may hold references to it), but it will invalidate the cache entry for the named module, causing Python to search anew for the named module upon its next import. The key can also be assigned to ``None``, forcing the next import -of the module to result in an :exc:`ImportError`. +of the module to result in an :exc:`ModuleNotFoundError`. Beware though, as if you keep a reference to the module object, invalidate its cache entry in :data:`sys.modules`, and then re-import the @@ -284,7 +284,7 @@ handle the named module or not. If the meta path finder knows how to handle the named module, it returns a loader object. If it cannot handle the named module, it returns ``None``. If :data:`sys.meta_path` processing reaches the end of its list without returning -a loader, then an :exc:`ImportError` is raised. Any other exceptions raised +a loader, then an :exc:`ModuleNotFoundError` is raised. Any other exceptions raised are simply propagated up, aborting the import process. The :meth:`find_module()` method of meta path finders is called with two @@ -647,7 +647,7 @@ import statements within that module. To selectively prevent import of some modules from a hook early on the meta path (rather than disabling the standard import system entirely), -it is sufficient to raise :exc:`ImportError` directly from +it is sufficient to raise :exc:`ModuleNotFoundError` directly from :meth:`find_module` instead of returning ``None``. The latter indicates that the meta path search should continue. while raising an exception terminates it immediately. diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst index 23ccae2..1054c68 100644 --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -269,3 +269,6 @@ that may require changes to your code. * Frozen packages no longer set ``__path__`` to a list containg the package name but an empty list instead. Determing if a module is a package should be done using ``hasattr(module, '__path__')``. + +* :c:func:`PyErr_SetImportError` now sets :exc:`TypeError` when its **msg** + argument is not set. Previously only ``NULL`` was returned. diff --git a/Include/pyerrors.h b/Include/pyerrors.h index 2e66c0e..22a22ce 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -268,6 +268,9 @@ PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErr(PyObject *, int); PyAPI_FUNC(PyObject *) PyErr_SetExcWithArgsKwargs(PyObject *, PyObject *, PyObject *); + +PyAPI_FUNC(PyObject *) PyErr_SetImportErrorSubclass(PyObject *, PyObject *, + PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyErr_SetImportError(PyObject *, PyObject *, PyObject *); diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index cd41336..9a82bd1 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -1614,7 +1614,7 @@ def _gcd_import(name, package=None, level=0): _imp.release_lock() message = ("import of {} halted; " "None in sys.modules".format(name)) - raise ImportError(message, name=name) + raise ModuleNotFoundError(message, name=name) _lock_unlock_module(name) return module @@ -443,6 +443,10 @@ Tests C-API ----- +- Issue #15767: Added PyErr_SetImportErrorSubclass(). + +- PyErr_SetImportError() now sets TypeError when its msg argument is set. + - Issue #9369: The types of `char*` arguments of PyObject_CallFunction() and PyObject_CallMethod() now changed to `const char*`. Based on patches by Jörg Müller and Lars Buitinck. diff --git a/Python/errors.c b/Python/errors.c index 1f955b5..89021aa 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -619,12 +619,25 @@ PyObject *PyErr_SetFromWindowsErrWithUnicodeFilename( #endif /* MS_WINDOWS */ PyObject * -PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path) +PyErr_SetImportErrorSubclass(PyObject *exception, PyObject *msg, + PyObject *name, PyObject *path) { + int issubclass; PyObject *args, *kwargs, *error; - if (msg == NULL) + issubclass = PyObject_IsSubclass(exception, PyExc_ImportError); + if (issubclass < 0) { + return NULL; + } + else if (!issubclass) { + PyErr_SetString(PyExc_TypeError, "expected a subclass of ImportError"); + return NULL; + } + + if (msg == NULL) { + PyErr_SetString(PyExc_TypeError, "expected a message argument"); return NULL; + } args = PyTuple_New(1); if (args == NULL) @@ -649,7 +662,7 @@ PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path) PyDict_SetItemString(kwargs, "name", name); PyDict_SetItemString(kwargs, "path", path); - error = PyObject_Call(PyExc_ImportError, args, kwargs); + error = PyObject_Call(exception, args, kwargs); if (error != NULL) { PyErr_SetObject((PyObject *)Py_TYPE(error), error); Py_DECREF(error); @@ -661,6 +674,12 @@ PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path) return NULL; } +PyObject * +PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path) +{ + return PyErr_SetImportErrorSubclass(PyExc_ImportError, msg, name, path); +} + void _PyErr_BadInternalCall(const char *filename, int lineno) { diff --git a/Python/import.c b/Python/import.c index 0bb46d2..fad54e6 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1436,7 +1436,8 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *given_globals, PyObject *msg = PyUnicode_FromFormat("import of %R halted; " "None in sys.modules", abs_name); if (msg != NULL) { - PyErr_SetImportError(msg, abs_name, NULL); + PyErr_SetImportErrorSubclass(PyExc_ModuleNotFoundError, msg, + abs_name, NULL); Py_DECREF(msg); } mod = NULL; diff --git a/Python/importlib.h b/Python/importlib.h index 4067631..e2f60c9 100644 --- a/Python/importlib.h +++ b/Python/importlib.h @@ -3239,8 +3239,8 @@ const unsigned char _Py_M__importlib[] = { 0,114,64,1,0,0,114,59,1,0,0,114,96,0,0,0, 114,56,1,0,0,114,7,0,0,0,114,145,0,0,0,114, 69,1,0,0,244,11,0,0,0,95,103,99,100,95,105,109, - 112,111,114,116,114,97,0,0,0,114,46,0,0,0,114,156, - 0,0,0,114,98,0,0,0,40,5,0,0,0,114,66,0, + 112,111,114,116,114,97,0,0,0,114,46,0,0,0,114,66, + 1,0,0,114,98,0,0,0,40,5,0,0,0,114,66,0, 0,0,114,57,1,0,0,114,58,1,0,0,114,160,0,0, 0,114,139,0,0,0,114,4,0,0,0,114,4,0,0,0, 114,5,0,0,0,114,70,1,0,0,61,6,0,0,115,26, |