mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	Issue #17636: Circular imports involving relative imports are now supported.
This commit is contained in:
		
							parent
							
								
									dfbeb160de
								
							
						
					
					
						commit
						0373a106a1
					
				
					 13 changed files with 85 additions and 3 deletions
				
			
		|  | @ -568,7 +568,7 @@ def tearDown(self): | ||||||
| 
 | 
 | ||||||
|     def test_relimport_star(self): |     def test_relimport_star(self): | ||||||
|         # This will import * from .test_import. |         # This will import * from .test_import. | ||||||
|         from . import relimport |         from .. import relimport | ||||||
|         self.assertTrue(hasattr(relimport, "RelativeImportTests")) |         self.assertTrue(hasattr(relimport, "RelativeImportTests")) | ||||||
| 
 | 
 | ||||||
|     def test_issue3221(self): |     def test_issue3221(self): | ||||||
|  | @ -1068,6 +1068,46 @@ def test_unencodable_filename(self): | ||||||
|                                        __isolated=False) |                                        __isolated=False) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class CircularImportTests(unittest.TestCase): | ||||||
|  | 
 | ||||||
|  |     """See the docstrings of the modules being imported for the purpose of the | ||||||
|  |     test.""" | ||||||
|  | 
 | ||||||
|  |     def tearDown(self): | ||||||
|  |         """Make sure no modules pre-exist in sys.modules which are being used to | ||||||
|  |         test.""" | ||||||
|  |         for key in list(sys.modules.keys()): | ||||||
|  |             if key.startswith('test.test_import.data.circular_imports'): | ||||||
|  |                 del sys.modules[key] | ||||||
|  | 
 | ||||||
|  |     def test_direct(self): | ||||||
|  |         try: | ||||||
|  |             import test.test_import.data.circular_imports.basic | ||||||
|  |         except ImportError: | ||||||
|  |             self.fail('circular import through relative imports failed') | ||||||
|  | 
 | ||||||
|  |     def test_indirect(self): | ||||||
|  |         try: | ||||||
|  |             import test.test_import.data.circular_imports.indirect | ||||||
|  |         except ImportError: | ||||||
|  |             self.fail('relative import in module contributing to circular ' | ||||||
|  |                       'import failed') | ||||||
|  | 
 | ||||||
|  |     def test_subpackage(self): | ||||||
|  |         try: | ||||||
|  |             import test.test_import.data.circular_imports.subpackage | ||||||
|  |         except ImportError: | ||||||
|  |             self.fail('circular import involving a subpackage failed') | ||||||
|  | 
 | ||||||
|  |     def test_rebinding(self): | ||||||
|  |         try: | ||||||
|  |             import test.test_import.data.circular_imports.rebinding as rebinding | ||||||
|  |         except ImportError: | ||||||
|  |             self.fail('circular import with rebinding of module attribute failed') | ||||||
|  |         from test.test_import.data.circular_imports.subpkg import util | ||||||
|  |         self.assertIs(util.util, rebinding.util) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     # Test needs to be a package, so we can do relative imports. |     # Test needs to be a package, so we can do relative imports. | ||||||
|     unittest.main() |     unittest.main() | ||||||
							
								
								
									
										3
									
								
								Lib/test/test_import/__main__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								Lib/test/test_import/__main__.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | unittest.main('test.test_import') | ||||||
							
								
								
									
										2
									
								
								Lib/test/test_import/data/circular_imports/basic.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								Lib/test/test_import/data/circular_imports/basic.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | ||||||
|  | """Circular imports through direct, relative imports.""" | ||||||
|  | from . import basic2 | ||||||
							
								
								
									
										1
									
								
								Lib/test/test_import/data/circular_imports/basic2.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Lib/test/test_import/data/circular_imports/basic2.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | from . import basic | ||||||
							
								
								
									
										1
									
								
								Lib/test/test_import/data/circular_imports/indirect.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Lib/test/test_import/data/circular_imports/indirect.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | from . import basic, basic2 | ||||||
							
								
								
									
										3
									
								
								Lib/test/test_import/data/circular_imports/rebinding.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								Lib/test/test_import/data/circular_imports/rebinding.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | """Test the binding of names when a circular import shares the same name as an | ||||||
|  | attribute.""" | ||||||
|  | from .rebinding2 import util | ||||||
							
								
								
									
										3
									
								
								Lib/test/test_import/data/circular_imports/rebinding2.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								Lib/test/test_import/data/circular_imports/rebinding2.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | from .subpkg import util | ||||||
|  | from . import rebinding | ||||||
|  | util = util.util | ||||||
							
								
								
									
										2
									
								
								Lib/test/test_import/data/circular_imports/subpackage.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								Lib/test/test_import/data/circular_imports/subpackage.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | ||||||
|  | """Circular import involving a sub-package.""" | ||||||
|  | from .subpkg import subpackage2 | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | #from .util import util | ||||||
|  | from .. import subpackage | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | def util(): | ||||||
|  |     pass | ||||||
							
								
								
									
										2
									
								
								Lib/test/test_import/data/circular_imports/util.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								Lib/test/test_import/data/circular_imports/util.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | ||||||
|  | def util(): | ||||||
|  |     pass | ||||||
|  | @ -10,6 +10,9 @@ Release date: TBA | ||||||
| Core and Builtins | Core and Builtins | ||||||
| ----------------- | ----------------- | ||||||
| 
 | 
 | ||||||
|  | - Issue #17636: Circular imports involving relative imports are now | ||||||
|  |   supported. | ||||||
|  | 
 | ||||||
| - Issue #22604: Fix assertion error in debug mode when dividing a complex | - Issue #22604: Fix assertion error in debug mode when dividing a complex | ||||||
|   number by (nan+0j). |   number by (nan+0j). | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4693,11 +4693,29 @@ static PyObject * | ||||||
| import_from(PyObject *v, PyObject *name) | import_from(PyObject *v, PyObject *name) | ||||||
| { | { | ||||||
|     PyObject *x; |     PyObject *x; | ||||||
|  |     _Py_IDENTIFIER(__name__); | ||||||
|  |     PyObject *fullmodname, *pkgname; | ||||||
| 
 | 
 | ||||||
|     x = PyObject_GetAttr(v, name); |     x = PyObject_GetAttr(v, name); | ||||||
|     if (x == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { |     if (x != NULL || !PyErr_ExceptionMatches(PyExc_AttributeError)) | ||||||
|  |         return x; | ||||||
|  |     /* Issue #17636: in case this failed because of a circular relative
 | ||||||
|  |        import, try to fallback on reading the module directly from | ||||||
|  |        sys.modules. */ | ||||||
|  |     PyErr_Clear(); | ||||||
|  |     pkgname = _PyObject_GetAttrId(v, &PyId___name__); | ||||||
|  |     if (pkgname == NULL) | ||||||
|  |         return NULL; | ||||||
|  |     fullmodname = PyUnicode_FromFormat("%U.%U", pkgname, name); | ||||||
|  |     Py_DECREF(pkgname); | ||||||
|  |     if (fullmodname == NULL) | ||||||
|  |         return NULL; | ||||||
|  |     x = PyDict_GetItem(PyImport_GetModuleDict(), fullmodname); | ||||||
|  |     if (x == NULL) | ||||||
|         PyErr_Format(PyExc_ImportError, "cannot import name %R", name); |         PyErr_Format(PyExc_ImportError, "cannot import name %R", name); | ||||||
|     } |     else | ||||||
|  |         Py_INCREF(x); | ||||||
|  |     Py_DECREF(fullmodname); | ||||||
|     return x; |     return x; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Antoine Pitrou
						Antoine Pitrou