Modified PyImport_Import and PyImport_ImportModule to always use absolute imports by calling __import__ with an explicit level of 0

Added a new API function PyImport_ImportModuleNoBlock. It solves the problem with dead locks when mixing threads and imports
This commit is contained in:
Christian Heimes 2008-01-03 22:16:32 +00:00
parent 0a83d79268
commit 000a074c95
19 changed files with 135 additions and 63 deletions

View file

@ -183,7 +183,8 @@ Importing Modules
single: __all__ (package variable) single: __all__ (package variable)
This is a simplified interface to :cfunc:`PyImport_ImportModuleEx` below, This is a simplified interface to :cfunc:`PyImport_ImportModuleEx` below,
leaving the *globals* and *locals* arguments set to *NULL*. When the *name* leaving the *globals* and *locals* arguments set to *NULL* and *level* set
to 0. When the *name*
argument contains a dot (when it specifies a submodule of a package), the argument contains a dot (when it specifies a submodule of a package), the
*fromlist* argument is set to the list ``['*']`` so that the return value is the *fromlist* argument is set to the list ``['*']`` so that the return value is the
named module rather than the top-level package containing it as would otherwise named module rather than the top-level package containing it as would otherwise
@ -198,9 +199,28 @@ Importing Modules
.. versionchanged:: 2.4 .. versionchanged:: 2.4
failing imports remove incomplete module objects. failing imports remove incomplete module objects.
.. versionchanged:: 2.6
always use absolute imports
.. index:: single: modules (in module sys) .. index:: single: modules (in module sys)
.. cfunction:: PyObject* PyImport_ImportModuleNoBlock(const char *name)
.. index::
single: `cfunc:PyImport_ImportModule`
This version of `cfunc:PyImport_ImportModule` does not block. It's intended
to be used in C function which import other modules to execute a function.
The import may block if another thread holds the import lock. The function
`cfunc:PyImport_ImportModuleNoBlock` doesn't block. It first tries to fetch
the module from sys.modules and falls back to `cfunc:PyImport_ImportModule`
unless the the lock is hold. In the latter case the function raises an
ImportError.
.. versionadded:: 2.6
.. cfunction:: PyObject* PyImport_ImportModuleEx(char *name, PyObject *globals, PyObject *locals, PyObject *fromlist) .. cfunction:: PyObject* PyImport_ImportModuleEx(char *name, PyObject *globals, PyObject *locals, PyObject *fromlist)
.. index:: builtin: __import__ .. index:: builtin: __import__
@ -218,6 +238,24 @@ Importing Modules
.. versionchanged:: 2.4 .. versionchanged:: 2.4
failing imports remove incomplete module objects. failing imports remove incomplete module objects.
.. versionchanged:: 2.6
The function is an alias for `cfunc:PyImport_ImportModuleLevel` with
-1 as level, meaning relative import.
.. cfunction:: PyObject* PyImport_ImportModuleLevel(char *name, PyObject *globals, PyObject *locals, PyObject *fromlist, int level)
Import a module. This is best described by referring to the built-in Python
function :func:`__import__`, as the standard :func:`__import__` function calls
this function directly.
The return value is a new reference to the imported module or top-level package,
or *NULL* with an exception set on failure. Like for :func:`__import__`,
the return value when a submodule of a package was requested is normally the
top-level package, unless a non-empty *fromlist* was given.
..versionadded:: 2.5
.. cfunction:: PyObject* PyImport_Import(PyObject *name) .. cfunction:: PyObject* PyImport_Import(PyObject *name)
@ -230,6 +268,9 @@ Importing Modules
current globals. This means that the import is done using whatever import hooks current globals. This means that the import is done using whatever import hooks
are installed in the current environment, e.g. by :mod:`rexec` or :mod:`ihooks`. are installed in the current environment, e.g. by :mod:`rexec` or :mod:`ihooks`.
.. versionchanged:: 2.6
always use absolute imports
.. cfunction:: PyObject* PyImport_ReloadModule(PyObject *m) .. cfunction:: PyObject* PyImport_ReloadModule(PyObject *m)

View file

@ -14,13 +14,10 @@ PyAPI_FUNC(PyObject *) PyImport_ExecCodeModuleEx(
PyAPI_FUNC(PyObject *) PyImport_GetModuleDict(void); PyAPI_FUNC(PyObject *) PyImport_GetModuleDict(void);
PyAPI_FUNC(PyObject *) PyImport_AddModule(const char *name); PyAPI_FUNC(PyObject *) PyImport_AddModule(const char *name);
PyAPI_FUNC(PyObject *) PyImport_ImportModule(const char *name); PyAPI_FUNC(PyObject *) PyImport_ImportModule(const char *name);
PyAPI_FUNC(PyObject *) PyImport_ImportModuleNoBlock(const char *);
PyAPI_FUNC(PyObject *) PyImport_ImportModuleLevel(char *name, PyAPI_FUNC(PyObject *) PyImport_ImportModuleLevel(char *name,
PyObject *globals, PyObject *locals, PyObject *fromlist, int level); PyObject *globals, PyObject *locals, PyObject *fromlist, int level);
/* For DLL compatibility */
#undef PyImport_ImportModuleEx
PyAPI_FUNC(PyObject *) PyImport_ImportModuleEx(
char *name, PyObject *globals, PyObject *locals, PyObject *fromlist);
#define PyImport_ImportModuleEx(n, g, l, f) \ #define PyImport_ImportModuleEx(n, g, l, f) \
PyImport_ImportModuleLevel(n, g, l, f, -1) PyImport_ImportModuleLevel(n, g, l, f, -1)

View file

@ -90,7 +90,7 @@ static void **PyCurses_API;
#define import_curses() \ #define import_curses() \
{ \ { \
PyObject *module = PyImport_ImportModule("_curses"); \ PyObject *module = PyImport_ImportModuleNoBlock("_curses"); \
if (module != NULL) { \ if (module != NULL) { \
PyObject *module_dict = PyModule_GetDict(module); \ PyObject *module_dict = PyModule_GetDict(module); \
PyObject *c_api_object = PyDict_GetItemString(module_dict, "_C_API"); \ PyObject *c_api_object = PyDict_GetItemString(module_dict, "_C_API"); \

View file

@ -357,7 +357,7 @@ MacOS_GetErrorString(PyObject *self, PyObject *args)
PyObject *m, *rv; PyObject *m, *rv;
errors_loaded = 1; errors_loaded = 1;
m = PyImport_ImportModule("macresource"); m = PyImport_ImportModuleNoBlock("macresource");
if (!m) { if (!m) {
if (Py_VerboseFlag) if (Py_VerboseFlag)
PyErr_Print(); PyErr_Print();

View file

@ -14,25 +14,10 @@ Core and builtins
- Issue #1640: Added math.isinf() and math.isnan() functions. - Issue #1640: Added math.isinf() and math.isnan() functions.
- Issue #1726: Remove Python/atof.c from PCBuild/pythoncore.vcproj
- Removed PCbuild8/ directory and added a new build directory for VS 2005
based on the VS 2008 build directory to PC/VS8.0. The script
PCbuild/vs8to9.py was added to sync changes from PCbuild to PC/VS8.0.
- Moved PCbuild/ directory for VS 2003 to PC/VS7.1 and renamed PCBuild9/
directory to PCBuild/.
- Issue #1629: Renamed Py_Size, Py_Type and Py_Refcnt to Py_SIZE, Py_TYPE
and Py_REFCNT.
- Issue #1635: Platform independent creation and representation of NaN - Issue #1635: Platform independent creation and representation of NaN
and INF. float("nan"), float("inf") and float("-inf") now work on every and INF. float("nan"), float("inf") and float("-inf") now work on every
platform with IEEE 754 semantics. platform with IEEE 754 semantics.
- Added case insensitive comparsion methods ``PyOS_stricmp(char*, char*)``
and ``PyOS_strnicmp(char*, char*, Py_ssize_t)``.
- Compiler now generates simpler and faster code for dictionary literals. - Compiler now generates simpler and faster code for dictionary literals.
The oparg for BUILD_MAP now indicates an estimated dictionary size. The oparg for BUILD_MAP now indicates an estimated dictionary size.
There is a new opcode, STORE_MAP, for adding entries to the dictionary. There is a new opcode, STORE_MAP, for adding entries to the dictionary.
@ -1165,6 +1150,15 @@ Tools/Demos
Build Build
----- -----
- Issue #1726: Remove Python/atof.c from PCBuild/pythoncore.vcproj
- Removed PCbuild8/ directory and added a new build directory for VS 2005
based on the VS 2008 build directory to PC/VS8.0. The script
PCbuild/vs8to9.py was added to sync changes from PCbuild to PC/VS8.0.
- Moved PCbuild/ directory for VS 2003 to PC/VS7.1 and renamed PCBuild9/
directory to PCBuild/.
- Bug #1699: Define _BSD_SOURCE only on OpenBSD. - Bug #1699: Define _BSD_SOURCE only on OpenBSD.
- Bug #1608: use -fwrapv when GCC supports it. This is important, - Bug #1608: use -fwrapv when GCC supports it. This is important,
@ -1225,6 +1219,18 @@ Build
C API C API
----- -----
- Added a new API function ``PyImport_ImportModuleNoBlock``.
- ``PyImport_Import`` and ``PyImport_ImportModule`` now always do absolute
imports. In earlier versions they might have used relative imports under
some conditions.
- Issue #1629: Renamed Py_Size, Py_Type and Py_Refcnt to Py_SIZE, Py_TYPE
and Py_REFCNT.
- Added case insensitive comparsion methods ``PyOS_stricmp(char*, char*)``
and ``PyOS_strnicmp(char*, char*, Py_ssize_t)``.
- Bug #1542693: remove semi-colon at end of PyImport_ImportModuleEx macro - Bug #1542693: remove semi-colon at end of PyImport_ImportModuleEx macro
so it can be used as an expression. so it can be used as an expression.

View file

@ -370,7 +370,7 @@ long Call_GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
if (context == NULL) if (context == NULL)
context = PyString_FromString("_ctypes.DllGetClassObject"); context = PyString_FromString("_ctypes.DllGetClassObject");
mod = PyImport_ImportModule("ctypes"); mod = PyImport_ImportModuleNoBlock("ctypes");
if (!mod) { if (!mod) {
PyErr_WriteUnraisable(context ? context : Py_None); PyErr_WriteUnraisable(context ? context : Py_None);
/* There has been a warning before about this already */ /* There has been a warning before about this already */
@ -449,7 +449,7 @@ long Call_CanUnloadNow(void)
if (context == NULL) if (context == NULL)
context = PyString_FromString("_ctypes.DllCanUnloadNow"); context = PyString_FromString("_ctypes.DllCanUnloadNow");
mod = PyImport_ImportModule("ctypes"); mod = PyImport_ImportModuleNoBlock("ctypes");
if (!mod) { if (!mod) {
/* OutputDebugString("Could not import ctypes"); */ /* OutputDebugString("Could not import ctypes"); */
/* We assume that this error can only occur when shutting /* We assume that this error can only occur when shutting

View file

@ -2255,7 +2255,7 @@ static int
update_lines_cols(void) update_lines_cols(void)
{ {
PyObject *o; PyObject *o;
PyObject *m = PyImport_ImportModule("curses"); PyObject *m = PyImport_ImportModuleNoBlock("curses");
if (!m) if (!m)
return 0; return 0;

View file

@ -245,7 +245,7 @@ getmultibytecodec(void)
static PyObject *cofunc = NULL; static PyObject *cofunc = NULL;
if (cofunc == NULL) { if (cofunc == NULL) {
PyObject *mod = PyImport_ImportModule("_multibytecodec"); PyObject *mod = PyImport_ImportModuleNoBlock("_multibytecodec");
if (mod == NULL) if (mod == NULL)
return NULL; return NULL;
cofunc = PyObject_GetAttrString(mod, "__create_codec"); cofunc = PyObject_GetAttrString(mod, "__create_codec");

View file

@ -1305,7 +1305,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
if (_PyString_Resize(&newfmt, usednew) < 0) if (_PyString_Resize(&newfmt, usednew) < 0)
goto Done; goto Done;
{ {
PyObject *time = PyImport_ImportModule("time"); PyObject *time = PyImport_ImportModuleNoBlock("time");
if (time == NULL) if (time == NULL)
goto Done; goto Done;
result = PyObject_CallMethod(time, "strftime", "OO", result = PyObject_CallMethod(time, "strftime", "OO",
@ -1353,7 +1353,7 @@ static PyObject *
time_time(void) time_time(void)
{ {
PyObject *result = NULL; PyObject *result = NULL;
PyObject *time = PyImport_ImportModule("time"); PyObject *time = PyImport_ImportModuleNoBlock("time");
if (time != NULL) { if (time != NULL) {
result = PyObject_CallMethod(time, "time", "()"); result = PyObject_CallMethod(time, "time", "()");
@ -1371,7 +1371,7 @@ build_struct_time(int y, int m, int d, int hh, int mm, int ss, int dstflag)
PyObject *time; PyObject *time;
PyObject *result = NULL; PyObject *result = NULL;
time = PyImport_ImportModule("time"); time = PyImport_ImportModuleNoBlock("time");
if (time != NULL) { if (time != NULL) {
result = PyObject_CallMethod(time, "struct_time", result = PyObject_CallMethod(time, "struct_time",
"((iiiiiiiii))", "((iiiiiiiii))",
@ -3827,7 +3827,7 @@ datetime_strptime(PyObject *cls, PyObject *args)
if (!PyArg_ParseTuple(args, "ss:strptime", &string, &format)) if (!PyArg_ParseTuple(args, "ss:strptime", &string, &format))
return NULL; return NULL;
if ((module = PyImport_ImportModule("time")) == NULL) if ((module = PyImport_ImportModuleNoBlock("time")) == NULL)
return NULL; return NULL;
obj = PyObject_CallMethod(module, "strptime", "ss", string, format); obj = PyObject_CallMethod(module, "strptime", "ss", string, format);
Py_DECREF(module); Py_DECREF(module);

View file

@ -1236,7 +1236,7 @@ initgc(void)
* the import and triggers an assertion. * the import and triggers an assertion.
*/ */
if (tmod == NULL) { if (tmod == NULL) {
tmod = PyImport_ImportModule("time"); tmod = PyImport_ImportModuleNoBlock("time");
if (tmod == NULL) if (tmod == NULL)
PyErr_Clear(); PyErr_Clear();
} }

View file

@ -3269,7 +3269,7 @@ initparser(void)
* If this fails, the import of this module will fail because an * If this fails, the import of this module will fail because an
* exception will be raised here; should we clear the exception? * exception will be raised here; should we clear the exception?
*/ */
copyreg = PyImport_ImportModule("copy_reg"); copyreg = PyImport_ImportModuleNoBlock("copy_reg");
if (copyreg != NULL) { if (copyreg != NULL) {
PyObject *func, *pickler; PyObject *func, *pickler;

View file

@ -5651,7 +5651,7 @@ wait_helper(int pid, int status, struct rusage *ru)
return posix_error(); return posix_error();
if (struct_rusage == NULL) { if (struct_rusage == NULL) {
PyObject *m = PyImport_ImportModule("resource"); PyObject *m = PyImport_ImportModuleNoBlock("resource");
if (m == NULL) if (m == NULL)
return NULL; return NULL;
struct_rusage = PyObject_GetAttrString(m, "struct_rusage"); struct_rusage = PyObject_GetAttrString(m, "struct_rusage");

View file

@ -222,7 +222,7 @@ int PySocketModule_ImportModuleAndAPI(void)
void *api; void *api;
DPRINTF("Importing the %s C API...\n", apimodule); DPRINTF("Importing the %s C API...\n", apimodule);
mod = PyImport_ImportModule(apimodule); mod = PyImport_ImportModuleNoBlock(apimodule);
if (mod == NULL) if (mod == NULL)
goto onError; goto onError;
DPRINTF(" %s package found\n", apimodule); DPRINTF(" %s package found\n", apimodule);

View file

@ -515,7 +515,7 @@ is not present, current time as returned by localtime() is used.");
static PyObject * static PyObject *
time_strptime(PyObject *self, PyObject *args) time_strptime(PyObject *self, PyObject *args)
{ {
PyObject *strptime_module = PyImport_ImportModule("_strptime"); PyObject *strptime_module = PyImport_ImportModuleNoBlock("_strptime");
PyObject *strptime_result; PyObject *strptime_result;
if (!strptime_module) if (!strptime_module)
@ -627,7 +627,7 @@ time_tzset(PyObject *self, PyObject *unused)
{ {
PyObject* m; PyObject* m;
m = PyImport_ImportModule("time"); m = PyImport_ImportModuleNoBlock("time");
if (m == NULL) { if (m == NULL) {
return NULL; return NULL;
} }

View file

@ -776,7 +776,7 @@ get_decompress_func(void)
let's avoid a stack overflow. */ let's avoid a stack overflow. */
return NULL; return NULL;
importing_zlib = 1; importing_zlib = 1;
zlib = PyImport_ImportModule("zlib"); /* import zlib */ zlib = PyImport_ImportModuleNoBlock("zlib");
importing_zlib = 0; importing_zlib = 0;
if (zlib != NULL) { if (zlib != NULL) {
decompress = PyObject_GetAttrString(zlib, decompress = PyObject_GetAttrString(zlib,

View file

@ -2216,7 +2216,7 @@ PyObject *PyUnicode_DecodeUnicodeEscape(const char *s,
if (ucnhash_CAPI == NULL) { if (ucnhash_CAPI == NULL) {
/* load the unicode data module */ /* load the unicode data module */
PyObject *m, *api; PyObject *m, *api;
m = PyImport_ImportModule("unicodedata"); m = PyImport_ImportModuleNoBlock("unicodedata");
if (m == NULL) if (m == NULL)
goto ucnhashError; goto ucnhashError;
api = PyObject_GetAttrString(m, "ucnhash_CAPI"); api = PyObject_GetAttrString(m, "ucnhash_CAPI");

View file

@ -690,7 +690,7 @@ PyErr_WarnExplicit(PyObject *category, const char *message,
{ {
PyObject *mod, *dict, *func = NULL; PyObject *mod, *dict, *func = NULL;
mod = PyImport_ImportModule("warnings"); mod = PyImport_ImportModuleNoBlock("warnings");
if (mod != NULL) { if (mod != NULL) {
dict = PyModule_GetDict(mod); dict = PyModule_GetDict(mod);
func = PyDict_GetItemString(dict, "warn_explicit"); func = PyDict_GetItemString(dict, "warn_explicit");

View file

@ -1985,6 +1985,53 @@ PyImport_ImportModule(const char *name)
return result; return result;
} }
/* Import a module without blocking
*
* At first it tries to fetch the module from sys.modules. If the module was
* never loaded before it loads it with PyImport_ImportModule() unless another
* thread holds the import lock. In the latter case the function raises an
* ImportError instead of blocking.
*
* Returns the module object with incremented ref count.
*/
PyObject *
PyImport_ImportModuleNoBlock(const char *name)
{
PyObject *result;
PyObject *modules;
long me;
/* Try to get the module from sys.modules[name] */
modules = PyImport_GetModuleDict();
if (modules == NULL)
return NULL;
result = PyDict_GetItemString(modules, name);
if (result != NULL) {
Py_INCREF(result);
return result;
}
else {
PyErr_Clear();
}
/* check the import lock
* me might be -1 but I ignore the error here, the lock function
* takes care of the problem */
me = PyThread_get_thread_ident();
if (import_lock_thread == -1 || import_lock_thread == me) {
/* no thread or me is holding the lock */
return PyImport_ImportModule(name);
}
else {
PyErr_Format(PyExc_ImportError,
"Failed to import %.200s because the import lock"
"is held by another thread.",
name);
return NULL;
}
}
/* Forward declarations for helper routines */ /* Forward declarations for helper routines */
static PyObject *get_parent(PyObject *globals, char *buf, static PyObject *get_parent(PyObject *globals, char *buf,
Py_ssize_t *p_buflen, int level); Py_ssize_t *p_buflen, int level);
@ -2054,26 +2101,6 @@ import_module_level(char *name, PyObject *globals, PyObject *locals,
return tail; return tail;
} }
/* For DLL compatibility */
#undef PyImport_ImportModuleEx
PyObject *
PyImport_ImportModuleEx(char *name, PyObject *globals, PyObject *locals,
PyObject *fromlist)
{
PyObject *result;
lock_import();
result = import_module_level(name, globals, locals, fromlist, -1);
if (unlock_import() < 0) {
Py_XDECREF(result);
PyErr_SetString(PyExc_RuntimeError,
"not holding the import lock");
return NULL;
}
return result;
}
#define PyImport_ImportModuleEx(n, g, l, f) \
PyImport_ImportModuleLevel(n, g, l, f, -1);
PyObject * PyObject *
PyImport_ImportModuleLevel(char *name, PyObject *globals, PyObject *locals, PyImport_ImportModuleLevel(char *name, PyObject *globals, PyObject *locals,
PyObject *fromlist, int level) PyObject *fromlist, int level)
@ -2646,9 +2673,10 @@ PyImport_Import(PyObject *module_name)
if (import == NULL) if (import == NULL)
goto err; goto err;
/* Call the __import__ function with the proper argument list */ /* Call the __import__ function with the proper argument list
r = PyObject_CallFunctionObjArgs(import, module_name, globals, * Always use absolute import here. */
globals, silly_list, NULL); r = PyObject_CallFunction(import, "OOOOi", module_name, globals,
globals, silly_list, 0, NULL);
err: err:
Py_XDECREF(globals); Py_XDECREF(globals);

View file

@ -36,7 +36,7 @@ PyMac_StrError(int err)
PyObject *m; PyObject *m;
PyObject *rv; PyObject *rv;
m = PyImport_ImportModule("MacOS"); m = PyImport_ImportModuleNoBlock("MacOS");
if (!m) { if (!m) {
if (Py_VerboseFlag) if (Py_VerboseFlag)
PyErr_Print(); PyErr_Print();