mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	with PEP 302. This was fixed by adding an ``imp.NullImporter`` type that is used in ``sys.path_importer_cache`` to cache non-directory paths and avoid excessive filesystem operations during imports.
This commit is contained in:
		
							parent
							
								
									944f3b6ecb
								
							
						
					
					
						commit
						f7575d0cb7
					
				
					 4 changed files with 161 additions and 38 deletions
				
			
		|  | @ -232,6 +232,24 @@ properly matching byte-compiled file (with suffix \file{.pyc} or | ||||||
| source file. | source file. | ||||||
| \end{funcdesc} | \end{funcdesc} | ||||||
| 
 | 
 | ||||||
|  | \begin{classdesc}{NullImporter}{path_string} | ||||||
|  | The \class{NullImporter} type is a \pep{302} import hook that handles | ||||||
|  | non-directory path strings by failing to find any modules.  Calling this | ||||||
|  | type with an existing directory or empty string raises | ||||||
|  | \exception{ImportError}.  Otherwise, a \class{NullImporter} instance is | ||||||
|  | returned. | ||||||
|  | 
 | ||||||
|  | Python adds instances of this type to \code{sys.path_importer_cache} for | ||||||
|  | any path entries that are not directories and are not handled by any other | ||||||
|  | path hooks on \code{sys.path_hooks}.  Instances have only one method: | ||||||
|  | 
 | ||||||
|  | \begin{methoddesc}{find_module}{fullname \optional{, path}} | ||||||
|  | This method always returns \code{None}, indicating that the requested | ||||||
|  | module could not be found. | ||||||
|  | \end{methoddesc} | ||||||
|  | 
 | ||||||
|  | \versionadded{2.5} | ||||||
|  | \end{classdesc} | ||||||
| 
 | 
 | ||||||
| \subsection{Examples} | \subsection{Examples} | ||||||
| \label{examples-imp} | \label{examples-imp} | ||||||
|  |  | ||||||
|  | @ -381,9 +381,7 @@ def get_importer(path_item): | ||||||
|             importer = None |             importer = None | ||||||
|         sys.path_importer_cache.setdefault(path_item, importer) |         sys.path_importer_cache.setdefault(path_item, importer) | ||||||
| 
 | 
 | ||||||
|     # The boolean values are used for caching valid and invalid |     if importer is None: | ||||||
|     # file paths for the built-in import machinery |  | ||||||
|     if importer in (None, True, False): |  | ||||||
|         try: |         try: | ||||||
|             importer = ImpImporter(path_item) |             importer = ImpImporter(path_item) | ||||||
|         except ImportError: |         except ImportError: | ||||||
|  |  | ||||||
|  | @ -12,6 +12,11 @@ What's New in Python 2.5 release candidate 1? | ||||||
| Core and builtins | Core and builtins | ||||||
| ----------------- | ----------------- | ||||||
| 
 | 
 | ||||||
|  | - Bug #1529871: The speed enhancement patch #921466 broke Python's compliance | ||||||
|  |   with PEP 302.  This was fixed by adding an ``imp.NullImporter`` type that is | ||||||
|  |   used in ``sys.path_importer_cache`` to cache non-directory paths and avoid | ||||||
|  |   excessive filesystem operations during imports. | ||||||
|  | 
 | ||||||
| - Bug #1521947: When checking for overflow, ``PyOS_strtol()`` used some | - Bug #1521947: When checking for overflow, ``PyOS_strtol()`` used some | ||||||
|   operations on signed longs that are formally undefined by C. |   operations on signed longs that are formally undefined by C. | ||||||
|   Unfortunately, at least one compiler now cares about that, so complicated |   Unfortunately, at least one compiler now cares about that, so complicated | ||||||
|  | @ -106,10 +111,14 @@ Library | ||||||
| Extension Modules | Extension Modules | ||||||
| ----------------- | ----------------- | ||||||
| 
 | 
 | ||||||
|  | <<<<<<< .mine | ||||||
|  | - Bug #1471938: Fix curses module build problem on Solaris 8; patch by | ||||||
|  | ======= | ||||||
| - The ``__reduce__()`` method of the new ``collections.defaultdict`` had | - The ``__reduce__()`` method of the new ``collections.defaultdict`` had | ||||||
|   a memory leak, affecting pickles and deep copies. |   a memory leak, affecting pickles and deep copies. | ||||||
| 
 | 
 | ||||||
| - Bug #1471938: Fix curses module build problem on Solaris 8; patch by | - Bug #1471938: Fix curses module build problem on Solaris 8; patch by | ||||||
|  | >>>>>>> .r50915 | ||||||
|   Paul Eggert. |   Paul Eggert. | ||||||
| 
 | 
 | ||||||
| - Patch #1448199: Release interpreter lock in _winreg.ConnectRegistry. | - Patch #1448199: Release interpreter lock in _winreg.ConnectRegistry. | ||||||
|  |  | ||||||
							
								
								
									
										166
									
								
								Python/import.c
									
										
									
									
									
								
							
							
						
						
									
										166
									
								
								Python/import.c
									
										
									
									
									
								
							|  | @ -98,6 +98,8 @@ static const struct filedescr _PyImport_StandardFiletab[] = { | ||||||
| }; | }; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | static PyTypeObject NullImporterType;	/* Forward reference */ | ||||||
|  | 
 | ||||||
| /* Initialize things */ | /* Initialize things */ | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
|  | @ -155,6 +157,8 @@ _PyImportHooks_Init(void) | ||||||
| 
 | 
 | ||||||
| 	/* adding sys.path_hooks and sys.path_importer_cache, setting up
 | 	/* adding sys.path_hooks and sys.path_importer_cache, setting up
 | ||||||
| 	   zipimport */ | 	   zipimport */ | ||||||
|  | 	if (PyType_Ready(&NullImporterType) < 0) | ||||||
|  | 		goto error; | ||||||
| 
 | 
 | ||||||
| 	if (Py_VerboseFlag) | 	if (Py_VerboseFlag) | ||||||
| 		PySys_WriteStderr("# installing zipimport hook\n"); | 		PySys_WriteStderr("# installing zipimport hook\n"); | ||||||
|  | @ -180,9 +184,11 @@ _PyImportHooks_Init(void) | ||||||
| 	if (err) { | 	if (err) { | ||||||
|   error: |   error: | ||||||
| 		PyErr_Print(); | 		PyErr_Print(); | ||||||
| 		Py_FatalError("initializing sys.meta_path, sys.path_hooks or " | 		Py_FatalError("initializing sys.meta_path, sys.path_hooks, " | ||||||
| 			      "path_importer_cache failed"); | 			      "path_importer_cache, or NullImporter failed" | ||||||
|  | 			      ); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
| 	zimpimport = PyImport_ImportModule("zipimport"); | 	zimpimport = PyImport_ImportModule("zipimport"); | ||||||
| 	if (zimpimport == NULL) { | 	if (zimpimport == NULL) { | ||||||
| 		PyErr_Clear(); /* No zip import module -- okay */ | 		PyErr_Clear(); /* No zip import module -- okay */ | ||||||
|  | @ -1058,9 +1064,18 @@ get_path_importer(PyObject *path_importer_cache, PyObject *path_hooks, | ||||||
| 		} | 		} | ||||||
| 		PyErr_Clear(); | 		PyErr_Clear(); | ||||||
| 	} | 	} | ||||||
| 	if (importer == NULL) | 	if (importer == NULL) { | ||||||
| 		importer = Py_None; | 		importer = PyObject_CallFunctionObjArgs( | ||||||
| 	else if (importer != Py_None) { | 			(PyObject *)&NullImporterType, p, NULL | ||||||
|  | 		); | ||||||
|  | 		if (importer == NULL) { | ||||||
|  | 			if (PyErr_ExceptionMatches(PyExc_ImportError)) { | ||||||
|  | 				PyErr_Clear(); | ||||||
|  | 				return Py_None; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if (importer != NULL) { | ||||||
| 		int err = PyDict_SetItem(path_importer_cache, p, importer); | 		int err = PyDict_SetItem(path_importer_cache, p, importer); | ||||||
| 		Py_DECREF(importer); | 		Py_DECREF(importer); | ||||||
| 		if (err != 0) | 		if (err != 0) | ||||||
|  | @ -1248,35 +1263,7 @@ find_module(char *fullname, char *subname, PyObject *path, char *buf, | ||||||
| 				return NULL; | 				return NULL; | ||||||
| 			} | 			} | ||||||
| 			/* Note: importer is a borrowed reference */ | 			/* Note: importer is a borrowed reference */ | ||||||
| 			if (importer == Py_False) { | 			if (importer != Py_None) { | ||||||
| 				/* Cached as not being a valid dir. */ |  | ||||||
| 				Py_XDECREF(copy); |  | ||||||
| 				continue; |  | ||||||
| 			} |  | ||||||
| 			else if (importer == Py_True) { |  | ||||||
| 				/* Cached as being a valid dir, so just
 |  | ||||||
| 				 * continue below. */ |  | ||||||
| 			} |  | ||||||
| 			else if (importer == Py_None) { |  | ||||||
| 				/* No importer was found, so it has to be a file.
 |  | ||||||
| 				 * Check if the directory is valid. |  | ||||||
| 				 * Note that the empty string is a valid path, but |  | ||||||
| 				 * not stat'able, hence the check for len. */ |  | ||||||
| #ifdef HAVE_STAT |  | ||||||
| 				if (len && stat(buf, &statbuf) != 0) { |  | ||||||
| 					/* Directory does not exist. */ |  | ||||||
| 					PyDict_SetItem(path_importer_cache, |  | ||||||
| 					               v, Py_False); |  | ||||||
| 					Py_XDECREF(copy); |  | ||||||
| 					continue; |  | ||||||
| 				} else { |  | ||||||
| 					PyDict_SetItem(path_importer_cache, |  | ||||||
| 					               v, Py_True); |  | ||||||
| 				} |  | ||||||
| #endif |  | ||||||
| 			} |  | ||||||
| 			else { |  | ||||||
| 				/* A real import hook importer was found. */ |  | ||||||
| 				PyObject *loader; | 				PyObject *loader; | ||||||
| 				loader = PyObject_CallMethod(importer, | 				loader = PyObject_CallMethod(importer, | ||||||
| 							     "find_module", | 							     "find_module", | ||||||
|  | @ -2935,11 +2922,120 @@ setint(PyObject *d, char *name, int value) | ||||||
| 	return err; | 	return err; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | typedef struct { | ||||||
|  |     PyObject_HEAD | ||||||
|  | } NullImporter; | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | NullImporter_init(NullImporter *self, PyObject *args, PyObject *kwds) | ||||||
|  | { | ||||||
|  | 	char *path; | ||||||
|  | 
 | ||||||
|  | 	if (!_PyArg_NoKeywords("NullImporter()", kwds)) | ||||||
|  | 		return -1; | ||||||
|  | 
 | ||||||
|  | 	if (!PyArg_ParseTuple(args, "s:NullImporter", | ||||||
|  | 			      &path)) | ||||||
|  | 		return -1; | ||||||
|  | 
 | ||||||
|  | 	if (strlen(path) == 0) { | ||||||
|  | 		PyErr_SetString(PyExc_ImportError, "empty pathname"); | ||||||
|  | 		return -1; | ||||||
|  | 	} else { | ||||||
|  | #ifndef RISCOS | ||||||
|  | 		struct stat statbuf; | ||||||
|  | 		int rv; | ||||||
|  | 
 | ||||||
|  | 		rv = stat(path, &statbuf); | ||||||
|  | 		if (rv == 0) { | ||||||
|  | 			/* it exists */ | ||||||
|  | 			if (S_ISDIR(statbuf.st_mode)) { | ||||||
|  | 				/* it's a directory */ | ||||||
|  | 				PyErr_SetString(PyExc_ImportError, | ||||||
|  | 						"existing directory"); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | #else | ||||||
|  | 		if (object_exists(path)) { | ||||||
|  | 			/* it exists */ | ||||||
|  | 			if (isdir(path)) { | ||||||
|  | 				/* it's a directory */ | ||||||
|  | 				PyErr_SetString(PyExc_ImportError, | ||||||
|  | 						"existing directory"); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | NullImporter_find_module(NullImporter *self, PyObject *args) | ||||||
|  | { | ||||||
|  | 	Py_RETURN_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyMethodDef NullImporter_methods[] = { | ||||||
|  | 	{"find_module", (PyCFunction)NullImporter_find_module, METH_VARARGS, | ||||||
|  | 	 "Always return None" | ||||||
|  | 	}, | ||||||
|  | 	{NULL}  /* Sentinel */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static PyTypeObject NullImporterType = { | ||||||
|  | 	PyObject_HEAD_INIT(NULL) | ||||||
|  | 	0,                         /*ob_size*/ | ||||||
|  | 	"imp.NullImporter",        /*tp_name*/ | ||||||
|  | 	sizeof(NullImporter),      /*tp_basicsize*/ | ||||||
|  | 	0,                         /*tp_itemsize*/ | ||||||
|  | 	0,                         /*tp_dealloc*/ | ||||||
|  | 	0,                         /*tp_print*/ | ||||||
|  | 	0,                         /*tp_getattr*/ | ||||||
|  | 	0,                         /*tp_setattr*/ | ||||||
|  | 	0,                         /*tp_compare*/ | ||||||
|  | 	0,                         /*tp_repr*/ | ||||||
|  | 	0,                         /*tp_as_number*/ | ||||||
|  | 	0,                         /*tp_as_sequence*/ | ||||||
|  | 	0,                         /*tp_as_mapping*/ | ||||||
|  | 	0,                         /*tp_hash */ | ||||||
|  | 	0,                         /*tp_call*/ | ||||||
|  | 	0,                         /*tp_str*/ | ||||||
|  | 	0,                         /*tp_getattro*/ | ||||||
|  | 	0,                         /*tp_setattro*/ | ||||||
|  | 	0,                         /*tp_as_buffer*/ | ||||||
|  | 	Py_TPFLAGS_DEFAULT,        /*tp_flags*/ | ||||||
|  | 	"Null importer object",    /* tp_doc */ | ||||||
|  | 	0,	                   /* tp_traverse */ | ||||||
|  | 	0,	                   /* tp_clear */ | ||||||
|  | 	0,	                   /* tp_richcompare */ | ||||||
|  | 	0,	                   /* tp_weaklistoffset */ | ||||||
|  | 	0,	                   /* tp_iter */ | ||||||
|  | 	0,	                   /* tp_iternext */ | ||||||
|  | 	NullImporter_methods,      /* tp_methods */ | ||||||
|  | 	0,                         /* tp_members */ | ||||||
|  | 	0,                         /* tp_getset */ | ||||||
|  | 	0,                         /* tp_base */ | ||||||
|  | 	0,                         /* tp_dict */ | ||||||
|  | 	0,                         /* tp_descr_get */ | ||||||
|  | 	0,                         /* tp_descr_set */ | ||||||
|  | 	0,                         /* tp_dictoffset */ | ||||||
|  | 	(initproc)NullImporter_init,      /* tp_init */ | ||||||
|  | 	0,                         /* tp_alloc */ | ||||||
|  | 	PyType_GenericNew          /* tp_new */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| PyMODINIT_FUNC | PyMODINIT_FUNC | ||||||
| initimp(void) | initimp(void) | ||||||
| { | { | ||||||
| 	PyObject *m, *d; | 	PyObject *m, *d; | ||||||
| 
 | 
 | ||||||
|  | 	if (PyType_Ready(&NullImporterType) < 0) | ||||||
|  | 		goto failure; | ||||||
|  | 
 | ||||||
| 	m = Py_InitModule4("imp", imp_methods, doc_imp, | 	m = Py_InitModule4("imp", imp_methods, doc_imp, | ||||||
| 			   NULL, PYTHON_API_VERSION); | 			   NULL, PYTHON_API_VERSION); | ||||||
| 	if (m == NULL) | 	if (m == NULL) | ||||||
|  | @ -2957,6 +3053,8 @@ initimp(void) | ||||||
| 	if (setint(d, "PY_CODERESOURCE", PY_CODERESOURCE) < 0) goto failure; | 	if (setint(d, "PY_CODERESOURCE", PY_CODERESOURCE) < 0) goto failure; | ||||||
| 	if (setint(d, "IMP_HOOK", IMP_HOOK) < 0) goto failure; | 	if (setint(d, "IMP_HOOK", IMP_HOOK) < 0) goto failure; | ||||||
| 
 | 
 | ||||||
|  | 	Py_INCREF(&NullImporterType); | ||||||
|  | 	PyModule_AddObject(m, "NullImporter", (PyObject *)&NullImporterType); | ||||||
|   failure: |   failure: | ||||||
| 	; | 	; | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Phillip J. Eby
						Phillip J. Eby