mirror of
				https://github.com/python/cpython.git
				synced 2025-10-30 21:21:22 +00:00 
			
		
		
		
	bpo-25782: avoid hang in PyErr_SetObject when current exception has a cycle in its context chain (GH-27626) (GH-27707)
Co-authored-by: Dennis Sweeney 36520290+sweeneyde@users.noreply.github.com
(cherry picked from commit d5c217475c)
Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
			
			
This commit is contained in:
		
							parent
							
								
									c7dfbd2f41
								
							
						
					
					
						commit
						6f4cdeddb9
					
				
					 3 changed files with 158 additions and 1 deletions
				
			
		|  | @ -923,6 +923,148 @@ def __del__(self): | |||
|             pass | ||||
|         self.assertEqual(e, (None, None, None)) | ||||
| 
 | ||||
|     def test_raise_does_not_create_context_chain_cycle(self): | ||||
|         class A(Exception): | ||||
|             pass | ||||
|         class B(Exception): | ||||
|             pass | ||||
|         class C(Exception): | ||||
|             pass | ||||
| 
 | ||||
|         # Create a context chain: | ||||
|         # C -> B -> A | ||||
|         # Then raise A in context of C. | ||||
|         try: | ||||
|             try: | ||||
|                 raise A | ||||
|             except A as a_: | ||||
|                 a = a_ | ||||
|                 try: | ||||
|                     raise B | ||||
|                 except B as b_: | ||||
|                     b = b_ | ||||
|                     try: | ||||
|                         raise C | ||||
|                     except C as c_: | ||||
|                         c = c_ | ||||
|                         self.assertIsInstance(a, A) | ||||
|                         self.assertIsInstance(b, B) | ||||
|                         self.assertIsInstance(c, C) | ||||
|                         self.assertIsNone(a.__context__) | ||||
|                         self.assertIs(b.__context__, a) | ||||
|                         self.assertIs(c.__context__, b) | ||||
|                         raise a | ||||
|         except A as e: | ||||
|             exc = e | ||||
| 
 | ||||
|         # Expect A -> C -> B, without cycle | ||||
|         self.assertIs(exc, a) | ||||
|         self.assertIs(a.__context__, c) | ||||
|         self.assertIs(c.__context__, b) | ||||
|         self.assertIsNone(b.__context__) | ||||
| 
 | ||||
|     def test_no_hang_on_context_chain_cycle1(self): | ||||
|         # See issue 25782. Cycle in context chain. | ||||
| 
 | ||||
|         def cycle(): | ||||
|             try: | ||||
|                 raise ValueError(1) | ||||
|             except ValueError as ex: | ||||
|                 ex.__context__ = ex | ||||
|                 raise TypeError(2) | ||||
| 
 | ||||
|         try: | ||||
|             cycle() | ||||
|         except Exception as e: | ||||
|             exc = e | ||||
| 
 | ||||
|         self.assertIsInstance(exc, TypeError) | ||||
|         self.assertIsInstance(exc.__context__, ValueError) | ||||
|         self.assertIs(exc.__context__.__context__, exc.__context__) | ||||
| 
 | ||||
|     def test_no_hang_on_context_chain_cycle2(self): | ||||
|         # See issue 25782. Cycle at head of context chain. | ||||
| 
 | ||||
|         class A(Exception): | ||||
|             pass | ||||
|         class B(Exception): | ||||
|             pass | ||||
|         class C(Exception): | ||||
|             pass | ||||
| 
 | ||||
|         # Context cycle: | ||||
|         # +-----------+ | ||||
|         # V           | | ||||
|         # C --> B --> A | ||||
|         with self.assertRaises(C) as cm: | ||||
|             try: | ||||
|                 raise A() | ||||
|             except A as _a: | ||||
|                 a = _a | ||||
|                 try: | ||||
|                     raise B() | ||||
|                 except B as _b: | ||||
|                     b = _b | ||||
|                     try: | ||||
|                         raise C() | ||||
|                     except C as _c: | ||||
|                         c = _c | ||||
|                         a.__context__ = c | ||||
|                         raise c | ||||
| 
 | ||||
|         self.assertIs(cm.exception, c) | ||||
|         # Verify the expected context chain cycle | ||||
|         self.assertIs(c.__context__, b) | ||||
|         self.assertIs(b.__context__, a) | ||||
|         self.assertIs(a.__context__, c) | ||||
| 
 | ||||
|     def test_no_hang_on_context_chain_cycle3(self): | ||||
|         # See issue 25782. Longer context chain with cycle. | ||||
| 
 | ||||
|         class A(Exception): | ||||
|             pass | ||||
|         class B(Exception): | ||||
|             pass | ||||
|         class C(Exception): | ||||
|             pass | ||||
|         class D(Exception): | ||||
|             pass | ||||
|         class E(Exception): | ||||
|             pass | ||||
| 
 | ||||
|         # Context cycle: | ||||
|         #             +-----------+ | ||||
|         #             V           | | ||||
|         # E --> D --> C --> B --> A | ||||
|         with self.assertRaises(E) as cm: | ||||
|             try: | ||||
|                 raise A() | ||||
|             except A as _a: | ||||
|                 a = _a | ||||
|                 try: | ||||
|                     raise B() | ||||
|                 except B as _b: | ||||
|                     b = _b | ||||
|                     try: | ||||
|                         raise C() | ||||
|                     except C as _c: | ||||
|                         c = _c | ||||
|                         a.__context__ = c | ||||
|                         try: | ||||
|                             raise D() | ||||
|                         except D as _d: | ||||
|                             d = _d | ||||
|                             e = E() | ||||
|                             raise e | ||||
| 
 | ||||
|         self.assertIs(cm.exception, e) | ||||
|         # Verify the expected context chain cycle | ||||
|         self.assertIs(e.__context__, d) | ||||
|         self.assertIs(d.__context__, c) | ||||
|         self.assertIs(c.__context__, b) | ||||
|         self.assertIs(b.__context__, a) | ||||
|         self.assertIs(a.__context__, c) | ||||
| 
 | ||||
|     def test_unicode_change_attributes(self): | ||||
|         # See issue 7309. This was a crasher. | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Miss Islington (bot)
						Miss Islington (bot)