mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	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:
		
							parent
							
								
									0a83d79268
								
							
						
					
					
						commit
						000a074c95
					
				
					 19 changed files with 135 additions and 63 deletions
				
			
		|  | @ -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) | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) | ||||
| 
 | ||||
|  |  | |||
|  | @ -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"); \ | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
							
								
								
									
										36
									
								
								Misc/NEWS
									
										
									
									
									
								
							
							
						
						
									
										36
									
								
								Misc/NEWS
									
										
									
									
									
								
							|  | @ -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. | ||||
| 
 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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"); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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(); | ||||
| 	} | ||||
|  |  | |||
|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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"); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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; | ||||
| 	} | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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"); | ||||
|  |  | |||
|  | @ -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"); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Christian Heimes
						Christian Heimes