Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8f5ac51

Browse files
committed
Issue #15767: Touch up ModuleNotFoundError usage by import.
Forgot to raise ModuleNotFoundError when None is found in sys.modules. This led to introducing the C function PyErr_SetImportErrorSubclass() to make setting ModuleNotFoundError easier. Also updated the reference docs to mention ModuleNotFoundError appropriately. Updated the docs for ModuleNotFoundError to mention the None in sys.modules case. Lastly, it was noticed that PyErr_SetImportError() was not setting an exception when returning None in one case. That issue is now fixed.
1 parent 3e9a9ae commit 8f5ac51

10 files changed

Lines changed: 52 additions & 14 deletions

File tree

Doc/c-api/exceptions.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,13 @@ in various ways. There is a separate error indicator for each thread.
292292
293293
.. versionadded:: 3.3
294294
295+
.. c:function:: PyObject* PyErr_SetImportErrorSubclass(PyObject *msg, PyObject *name, PyObject *path)
296+
297+
Much like :c:func:`PyErr_SetImportError` but this function allows for
298+
specifying a subclass of :exc:`ImportError` to raise.
299+
300+
.. versionadded:: 3.4
301+
295302
296303
.. c:function:: void PyErr_SyntaxLocationEx(char *filename, int lineno, int col_offset)
297304

Doc/library/exceptions.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ The following exceptions are the exceptions that are usually raised.
185185
A subclass of :exc:`ImportError` which is raised by :keyword:`import` when a
186186
module could not be located. This includes ``from ... import`` statements as
187187
the specific attribute being requested cannot be known a priori to be a module
188-
or some other type of object.
188+
or some other type of object. It is also raised when ``None`` is found in
189+
:data:`sys.modules`.
189190

190191
.. versionadded:: 3.4
191192

Doc/reference/import.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use the standard import system.
3737

3838
When a module is first imported, Python searches for the module and if found,
3939
it creates a module object [#fnmo]_, initializing it. If the named module
40-
cannot be found, an :exc:`ImportError` is raised. Python implements various
40+
cannot be found, an :exc:`ModuleNotFoundError` is raised. Python implements various
4141
strategies to search for the named module when the import machinery is
4242
invoked. These strategies can be modified and extended by using various hooks
4343
described in the sections below.
@@ -168,7 +168,7 @@ arguments to the :keyword:`import` statement, or from the parameters to the
168168
This name will be used in various phases of the import search, and it may be
169169
the dotted path to a submodule, e.g. ``foo.bar.baz``. In this case, Python
170170
first tries to import ``foo``, then ``foo.bar``, and finally ``foo.bar.baz``.
171-
If any of the intermediate imports fail, an :exc:`ImportError` is raised.
171+
If any of the intermediate imports fail, an :exc:`ModuleNotFoundError` is raised.
172172

173173

174174
The module cache
@@ -187,15 +187,15 @@ object.
187187
During import, the module name is looked up in :data:`sys.modules` and if
188188
present, the associated value is the module satisfying the import, and the
189189
process completes. However, if the value is ``None``, then an
190-
:exc:`ImportError` is raised. If the module name is missing, Python will
190+
:exc:`ModuleNotFoundError` is raised. If the module name is missing, Python will
191191
continue searching for the module.
192192

193193
:data:`sys.modules` is writable. Deleting a key may not destroy the
194194
associated module (as other modules may hold references to it),
195195
but it will invalidate the cache entry for the named module, causing
196196
Python to search anew for the named module upon its next
197197
import. The key can also be assigned to ``None``, forcing the next import
198-
of the module to result in an :exc:`ImportError`.
198+
of the module to result in an :exc:`ModuleNotFoundError`.
199199

200200
Beware though, as if you keep a reference to the module object,
201201
invalidate its cache entry in :data:`sys.modules`, and then re-import the
@@ -284,7 +284,7 @@ handle the named module or not.
284284
If the meta path finder knows how to handle the named module, it returns a
285285
loader object. If it cannot handle the named module, it returns ``None``. If
286286
:data:`sys.meta_path` processing reaches the end of its list without returning
287-
a loader, then an :exc:`ImportError` is raised. Any other exceptions raised
287+
a loader, then an :exc:`ModuleNotFoundError` is raised. Any other exceptions raised
288288
are simply propagated up, aborting the import process.
289289

290290
The :meth:`find_module()` method of meta path finders is called with two
@@ -647,7 +647,7 @@ import statements within that module.
647647

648648
To selectively prevent import of some modules from a hook early on the
649649
meta path (rather than disabling the standard import system entirely),
650-
it is sufficient to raise :exc:`ImportError` directly from
650+
it is sufficient to raise :exc:`ModuleNotFoundError` directly from
651651
:meth:`find_module` instead of returning ``None``. The latter indicates
652652
that the meta path search should continue. while raising an exception
653653
terminates it immediately.

Doc/whatsnew/3.4.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,3 +269,6 @@ that may require changes to your code.
269269
* Frozen packages no longer set ``__path__`` to a list containg the package name
270270
but an empty list instead. Determing if a module is a package should be done
271271
using ``hasattr(module, '__path__')``.
272+
273+
* :c:func:`PyErr_SetImportError` now sets :exc:`TypeError` when its **msg**
274+
argument is not set. Previously only ``NULL`` was returned.

Include/pyerrors.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErr(PyObject *, int);
268268

269269
PyAPI_FUNC(PyObject *) PyErr_SetExcWithArgsKwargs(PyObject *, PyObject *,
270270
PyObject *);
271+
272+
PyAPI_FUNC(PyObject *) PyErr_SetImportErrorSubclass(PyObject *, PyObject *,
273+
PyObject *, PyObject *);
271274
PyAPI_FUNC(PyObject *) PyErr_SetImportError(PyObject *, PyObject *,
272275
PyObject *);
273276

Lib/importlib/_bootstrap.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1614,7 +1614,7 @@ def _gcd_import(name, package=None, level=0):
16141614
_imp.release_lock()
16151615
message = ("import of {} halted; "
16161616
"None in sys.modules".format(name))
1617-
raise ImportError(message, name=name)
1617+
raise ModuleNotFoundError(message, name=name)
16181618
_lock_unlock_module(name)
16191619
return module
16201620

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,10 @@ Tests
443443
C-API
444444
-----
445445

446+
- Issue #15767: Added PyErr_SetImportErrorSubclass().
447+
448+
- PyErr_SetImportError() now sets TypeError when its msg argument is set.
449+
446450
- Issue #9369: The types of `char*` arguments of PyObject_CallFunction() and
447451
PyObject_CallMethod() now changed to `const char*`. Based on patches by
448452
Jörg Müller and Lars Buitinck.

Python/errors.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -619,12 +619,25 @@ PyObject *PyErr_SetFromWindowsErrWithUnicodeFilename(
619619
#endif /* MS_WINDOWS */
620620

621621
PyObject *
622-
PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path)
622+
PyErr_SetImportErrorSubclass(PyObject *exception, PyObject *msg,
623+
PyObject *name, PyObject *path)
623624
{
625+
int issubclass;
624626
PyObject *args, *kwargs, *error;
625627

626-
if (msg == NULL)
628+
issubclass = PyObject_IsSubclass(exception, PyExc_ImportError);
629+
if (issubclass < 0) {
630+
return NULL;
631+
}
632+
else if (!issubclass) {
633+
PyErr_SetString(PyExc_TypeError, "expected a subclass of ImportError");
634+
return NULL;
635+
}
636+
637+
if (msg == NULL) {
638+
PyErr_SetString(PyExc_TypeError, "expected a message argument");
627639
return NULL;
640+
}
628641

629642
args = PyTuple_New(1);
630643
if (args == NULL)
@@ -649,7 +662,7 @@ PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path)
649662
PyDict_SetItemString(kwargs, "name", name);
650663
PyDict_SetItemString(kwargs, "path", path);
651664

652-
error = PyObject_Call(PyExc_ImportError, args, kwargs);
665+
error = PyObject_Call(exception, args, kwargs);
653666
if (error != NULL) {
654667
PyErr_SetObject((PyObject *)Py_TYPE(error), error);
655668
Py_DECREF(error);
@@ -661,6 +674,12 @@ PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path)
661674
return NULL;
662675
}
663676

677+
PyObject *
678+
PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path)
679+
{
680+
return PyErr_SetImportErrorSubclass(PyExc_ImportError, msg, name, path);
681+
}
682+
664683
void
665684
_PyErr_BadInternalCall(const char *filename, int lineno)
666685
{

Python/import.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1436,7 +1436,8 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *given_globals,
14361436
PyObject *msg = PyUnicode_FromFormat("import of %R halted; "
14371437
"None in sys.modules", abs_name);
14381438
if (msg != NULL) {
1439-
PyErr_SetImportError(msg, abs_name, NULL);
1439+
PyErr_SetImportErrorSubclass(PyExc_ModuleNotFoundError, msg,
1440+
abs_name, NULL);
14401441
Py_DECREF(msg);
14411442
}
14421443
mod = NULL;

Python/importlib.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3239,8 +3239,8 @@ const unsigned char _Py_M__importlib[] = {
32393239
0,114,64,1,0,0,114,59,1,0,0,114,96,0,0,0,
32403240
114,56,1,0,0,114,7,0,0,0,114,145,0,0,0,114,
32413241
69,1,0,0,244,11,0,0,0,95,103,99,100,95,105,109,
3242-
112,111,114,116,114,97,0,0,0,114,46,0,0,0,114,156,
3243-
0,0,0,114,98,0,0,0,40,5,0,0,0,114,66,0,
3242+
112,111,114,116,114,97,0,0,0,114,46,0,0,0,114,66,
3243+
1,0,0,114,98,0,0,0,40,5,0,0,0,114,66,0,
32443244
0,0,114,57,1,0,0,114,58,1,0,0,114,160,0,0,
32453245
0,114,139,0,0,0,114,4,0,0,0,114,4,0,0,0,
32463246
114,5,0,0,0,114,70,1,0,0,61,6,0,0,115,26,

0 commit comments

Comments
 (0)