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)
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
*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
@ -198,9 +199,28 @@ Importing Modules
.. versionchanged:: 2.4
failing imports remove incomplete module objects.
.. versionchanged:: 2.6
always use absolute imports
.. 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)
.. index:: builtin: __import__
@ -218,6 +238,24 @@ Importing Modules
.. versionchanged:: 2.4
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)
@ -230,6 +268,9 @@ Importing Modules
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`.
.. versionchanged:: 2.6
always use absolute imports
.. 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_AddModule(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,
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) \
PyImport_ImportModuleLevel(n, g, l, f, -1)

View file

@ -90,7 +90,7 @@ static void **PyCurses_API;
#define import_curses() \
{ \
PyObject *module = PyImport_ImportModule("_curses"); \
PyObject *module = PyImport_ImportModuleNoBlock("_curses"); \
if (module != NULL) { \
PyObject *module_dict = PyModule_GetDict(module); \
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;
errors_loaded = 1;
m = PyImport_ImportModule("macresource");
m = PyImport_ImportModuleNoBlock("macresource");
if (!m) {
if (Py_VerboseFlag)
PyErr_Print();

View file

@ -14,25 +14,10 @@ Core and builtins
- 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
and INF. float("nan"), float("inf") and float("-inf") now work on every
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.
The oparg for BUILD_MAP now indicates an estimated dictionary size.
There is a new opcode, STORE_MAP, for adding entries to the dictionary.
@ -1165,6 +1150,15 @@ Tools/Demos
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 #1608: use -fwrapv when GCC supports it. This is important,
@ -1225,6 +1219,18 @@ Build
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
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)
context = PyString_FromString("_ctypes.DllGetClassObject");
mod = PyImport_ImportModule("ctypes");
mod = PyImport_ImportModuleNoBlock("ctypes");
if (!mod) {
PyErr_WriteUnraisable(context ? context : Py_None);
/* There has been a warning before about this already */
@ -449,7 +449,7 @@ long Call_CanUnloadNow(void)
if (context == NULL)
context = PyString_FromString("_ctypes.DllCanUnloadNow");
mod = PyImport_ImportModule("ctypes");
mod = PyImport_ImportModuleNoBlock("ctypes");
if (!mod) {
/* OutputDebugString("Could not import ctypes"); */
/* We assume that this error can only occur when shutting

View file

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

View file

@ -245,7 +245,7 @@ getmultibytecodec(void)
static PyObject *cofunc = NULL;
if (cofunc == NULL) {
PyObject *mod = PyImport_ImportModule("_multibytecodec");
PyObject *mod = PyImport_ImportModuleNoBlock("_multibytecodec");
if (mod == NULL)
return NULL;
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)
goto Done;
{
PyObject *time = PyImport_ImportModule("time");
PyObject *time = PyImport_ImportModuleNoBlock("time");
if (time == NULL)
goto Done;
result = PyObject_CallMethod(time, "strftime", "OO",
@ -1353,7 +1353,7 @@ static PyObject *
time_time(void)
{
PyObject *result = NULL;
PyObject *time = PyImport_ImportModule("time");
PyObject *time = PyImport_ImportModuleNoBlock("time");
if (time != NULL) {
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 *result = NULL;
time = PyImport_ImportModule("time");
time = PyImport_ImportModuleNoBlock("time");
if (time != NULL) {
result = PyObject_CallMethod(time, "struct_time",
"((iiiiiiiii))",
@ -3827,7 +3827,7 @@ datetime_strptime(PyObject *cls, PyObject *args)
if (!PyArg_ParseTuple(args, "ss:strptime", &string, &format))
return NULL;
if ((module = PyImport_ImportModule("time")) == NULL)
if ((module = PyImport_ImportModuleNoBlock("time")) == NULL)
return NULL;
obj = PyObject_CallMethod(module, "strptime", "ss", string, format);
Py_DECREF(module);

View file

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

View file

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

View file

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

View file

@ -222,7 +222,7 @@ int PySocketModule_ImportModuleAndAPI(void)
void *api;
DPRINTF("Importing the %s C API...\n", apimodule);
mod = PyImport_ImportModule(apimodule);
mod = PyImport_ImportModuleNoBlock(apimodule);
if (mod == NULL)
goto onError;
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 *
time_strptime(PyObject *self, PyObject *args)
{
PyObject *strptime_module = PyImport_ImportModule("_strptime");
PyObject *strptime_module = PyImport_ImportModuleNoBlock("_strptime");
PyObject *strptime_result;
if (!strptime_module)
@ -627,7 +627,7 @@ time_tzset(PyObject *self, PyObject *unused)
{
PyObject* m;
m = PyImport_ImportModule("time");
m = PyImport_ImportModuleNoBlock("time");
if (m == NULL) {
return NULL;
}

View file

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

View file

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

View file

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

View file

@ -1985,6 +1985,53 @@ 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
* 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 */
static PyObject *get_parent(PyObject *globals, char *buf,
Py_ssize_t *p_buflen, int level);
@ -2054,26 +2101,6 @@ import_module_level(char *name, PyObject *globals, PyObject *locals,
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 *
PyImport_ImportModuleLevel(char *name, PyObject *globals, PyObject *locals,
PyObject *fromlist, int level)
@ -2646,9 +2673,10 @@ PyImport_Import(PyObject *module_name)
if (import == NULL)
goto err;
/* Call the __import__ function with the proper argument list */
r = PyObject_CallFunctionObjArgs(import, module_name, globals,
globals, silly_list, NULL);
/* Call the __import__ function with the proper argument list
* Always use absolute import here. */
r = PyObject_CallFunction(import, "OOOOi", module_name, globals,
globals, silly_list, 0, NULL);
err:
Py_XDECREF(globals);

View file

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