-
-
Notifications
You must be signed in to change notification settings - Fork 34.7k
bpo-35943: PyImport_GetModule() can return partially-initialized module #15057
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c374e0a
01fe079
7d1f7ee
f2be697
0925812
57a6231
6c1c28e
13f438e
fc83ad3
ff771e7
57dbdf5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| The function :c:func:`PyImport_GetModule` now ensures any module it returns is fully initialized. | ||
| Patch by Joannah Nanjekye. |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -387,11 +387,33 @@ import_get_module(PyThreadState *tstate, PyObject *name) | |||||||||||
| } | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| PyObject * | ||||||||||||
| PyImport_GetModule(PyObject *name) | ||||||||||||
| static int | ||||||||||||
| import_ensure_initialized(PyThreadState *tstate, PyObject *mod, PyObject *name) | ||||||||||||
| { | ||||||||||||
| PyThreadState *tstate = _PyThreadState_GET(); | ||||||||||||
| return import_get_module(tstate, name); | ||||||||||||
| PyInterpreterState *interp = tstate->interp; | ||||||||||||
| PyObject *spec; | ||||||||||||
|
|
||||||||||||
| _Py_IDENTIFIER(__spec__); | ||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please move both these statics to the top of the file (next to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be better to make that as a separate change. This style where they're block-local is the way the existing code works, which keeps the diff simple. It's also the way that every use of
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sounds good |
||||||||||||
| _Py_IDENTIFIER(_lock_unlock_module); | ||||||||||||
|
|
||||||||||||
| /* Optimization: only call _bootstrap._lock_unlock_module() if | ||||||||||||
| __spec__._initializing is true. | ||||||||||||
| NOTE: because of this, initializing must be set *before* | ||||||||||||
| stuffing the new module in sys.modules. | ||||||||||||
| */ | ||||||||||||
| spec = _PyObject_GetAttrId(mod, &PyId___spec__); | ||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||
| int busy = _PyModuleSpec_IsInitializing(spec); | ||||||||||||
| Py_XDECREF(spec); | ||||||||||||
| if (busy) { | ||||||||||||
| /* Wait until module is done importing. */ | ||||||||||||
| PyObject *value = _PyObject_CallMethodIdOneArg( | ||||||||||||
|
brettcannon marked this conversation as resolved.
|
||||||||||||
| interp->importlib, &PyId__lock_unlock_module, name); | ||||||||||||
| if (value == NULL) { | ||||||||||||
| return -1; | ||||||||||||
| } | ||||||||||||
| Py_DECREF(value); | ||||||||||||
|
serhiy-storchaka marked this conversation as resolved.
|
||||||||||||
| } | ||||||||||||
| return 0; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
|
|
@@ -1461,6 +1483,7 @@ PyImport_ImportModule(const char *name) | |||||||||||
| return result; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| /* Import a module without blocking | ||||||||||||
| * | ||||||||||||
| * At first it tries to fetch the module from sys.modules. If the module was | ||||||||||||
|
|
@@ -1760,6 +1783,23 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name) | |||||||||||
| return mod; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| PyObject * | ||||||||||||
| PyImport_GetModule(PyObject *name) | ||||||||||||
| { | ||||||||||||
| PyThreadState *tstate = _PyThreadState_GET(); | ||||||||||||
| PyObject *mod; | ||||||||||||
|
|
||||||||||||
| mod = import_get_module(tstate, name); | ||||||||||||
| if (mod != NULL && mod != Py_None) { | ||||||||||||
| if (import_ensure_initialized(tstate, mod, name) < 0) { | ||||||||||||
| Py_DECREF(mod); | ||||||||||||
| remove_importlib_frames(tstate); | ||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What made you add this
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @serhiy-storchaka had requested it -- I think most likely because it's found in the corresponding |
||||||||||||
| return NULL; | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| return mod; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| PyObject * | ||||||||||||
| PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, | ||||||||||||
| PyObject *locals, PyObject *fromlist, | ||||||||||||
|
|
@@ -1815,26 +1855,9 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, | |||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if (mod != NULL && mod != Py_None) { | ||||||||||||
| _Py_IDENTIFIER(__spec__); | ||||||||||||
| _Py_IDENTIFIER(_lock_unlock_module); | ||||||||||||
| PyObject *spec; | ||||||||||||
|
|
||||||||||||
| /* Optimization: only call _bootstrap._lock_unlock_module() if | ||||||||||||
| __spec__._initializing is true. | ||||||||||||
| NOTE: because of this, initializing must be set *before* | ||||||||||||
| stuffing the new module in sys.modules. | ||||||||||||
| */ | ||||||||||||
| spec = _PyObject_GetAttrId(mod, &PyId___spec__); | ||||||||||||
| if (_PyModuleSpec_IsInitializing(spec)) { | ||||||||||||
| PyObject *value = _PyObject_CallMethodIdOneArg( | ||||||||||||
| interp->importlib, &PyId__lock_unlock_module, abs_name); | ||||||||||||
| if (value == NULL) { | ||||||||||||
| Py_DECREF(spec); | ||||||||||||
| goto error; | ||||||||||||
| } | ||||||||||||
| Py_DECREF(value); | ||||||||||||
| if (import_ensure_initialized(tstate, mod, name) < 0) { | ||||||||||||
| goto error; | ||||||||||||
| } | ||||||||||||
| Py_XDECREF(spec); | ||||||||||||
| } | ||||||||||||
| else { | ||||||||||||
| Py_XDECREF(mod); | ||||||||||||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, I always prefer to pass in as little as possible. Here
PyThreadStateis quite broad, since we only needPyInterpreterState.importlib. At the same time, for smaller code like this it can help to keep the common indirections in the function. So I guess I'd leave it as-is. :)