mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	Implicit exception chaining via __context__ (PEP 3134).
Patch 3108 by Antooine Pitrou.
This commit is contained in:
		
							parent
							
								
									973124fd70
								
							
						
					
					
						commit
						b4fb6e4d27
					
				
					 4 changed files with 139 additions and 33 deletions
				
			
		|  | @ -480,7 +480,12 @@ def inner_raising_func(): | ||||||
|                 inner_raising_func() |                 inner_raising_func() | ||||||
|             except: |             except: | ||||||
|                 raise KeyError |                 raise KeyError | ||||||
|         except KeyError: |         except KeyError as e: | ||||||
|  |             # We want to test that the except block above got rid of | ||||||
|  |             # the exception raised in inner_raising_func(), but it | ||||||
|  |             # also ends up in the __context__ of the KeyError, so we | ||||||
|  |             # must clear the latter manually for our test to succeed. | ||||||
|  |             e.__context__ = None | ||||||
|             obj = None |             obj = None | ||||||
|             obj = wr() |             obj = wr() | ||||||
|             self.failUnless(obj is None, "%s" % obj) |             self.failUnless(obj is None, "%s" % obj) | ||||||
|  |  | ||||||
|  | @ -181,32 +181,102 @@ def test_accepts_traceback(self): | ||||||
|             self.fail("No exception raised") |             self.fail("No exception raised") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # Disabled until context is implemented | class TestContext(unittest.TestCase): | ||||||
| # class TestContext(object): |     def test_instance_context_instance_raise(self): | ||||||
| #     def test_instance_context_bare_raise(self): |         context = IndexError() | ||||||
| #         context = IndexError() |         try: | ||||||
| #         try: |             try: | ||||||
| #             try: |                 raise context | ||||||
| #                 raise context |             except: | ||||||
| #             except: |                 raise OSError() | ||||||
| #                 raise OSError() |         except OSError as e: | ||||||
| #         except OSError as e: |             self.assertEqual(e.__context__, context) | ||||||
| #             self.assertEqual(e.__context__, context) |         else: | ||||||
| #         else: |             self.fail("No exception raised") | ||||||
| #             self.fail("No exception raised") | 
 | ||||||
| # |     def test_class_context_instance_raise(self): | ||||||
| #     def test_class_context_bare_raise(self): |         context = IndexError | ||||||
| #         context = IndexError |         try: | ||||||
| #         try: |             try: | ||||||
| #             try: |                 raise context | ||||||
| #                 raise context |             except: | ||||||
| #             except: |                 raise OSError() | ||||||
| #                 raise OSError() |         except OSError as e: | ||||||
| #         except OSError as e: |             self.assertNotEqual(e.__context__, context) | ||||||
| #             self.assertNotEqual(e.__context__, context) |             self.failUnless(isinstance(e.__context__, context)) | ||||||
| #             self.failUnless(isinstance(e.__context__, context)) |         else: | ||||||
| #         else: |             self.fail("No exception raised") | ||||||
| #             self.fail("No exception raised") | 
 | ||||||
|  |     def test_class_context_class_raise(self): | ||||||
|  |         context = IndexError | ||||||
|  |         try: | ||||||
|  |             try: | ||||||
|  |                 raise context | ||||||
|  |             except: | ||||||
|  |                 raise OSError | ||||||
|  |         except OSError as e: | ||||||
|  |             self.assertNotEqual(e.__context__, context) | ||||||
|  |             self.failUnless(isinstance(e.__context__, context)) | ||||||
|  |         else: | ||||||
|  |             self.fail("No exception raised") | ||||||
|  | 
 | ||||||
|  |     def test_c_exception_context(self): | ||||||
|  |         try: | ||||||
|  |             try: | ||||||
|  |                 1/0 | ||||||
|  |             except: | ||||||
|  |                 raise OSError | ||||||
|  |         except OSError as e: | ||||||
|  |             self.failUnless(isinstance(e.__context__, ZeroDivisionError)) | ||||||
|  |         else: | ||||||
|  |             self.fail("No exception raised") | ||||||
|  | 
 | ||||||
|  |     def test_c_exception_raise(self): | ||||||
|  |         try: | ||||||
|  |             try: | ||||||
|  |                 1/0 | ||||||
|  |             except: | ||||||
|  |                 xyzzy | ||||||
|  |         except NameError as e: | ||||||
|  |             self.failUnless(isinstance(e.__context__, ZeroDivisionError)) | ||||||
|  |         else: | ||||||
|  |             self.fail("No exception raised") | ||||||
|  | 
 | ||||||
|  |     def test_noraise_finally(self): | ||||||
|  |         try: | ||||||
|  |             try: | ||||||
|  |                 pass | ||||||
|  |             finally: | ||||||
|  |                 raise OSError | ||||||
|  |         except OSError as e: | ||||||
|  |             self.failUnless(e.__context__ is None) | ||||||
|  |         else: | ||||||
|  |             self.fail("No exception raised") | ||||||
|  | 
 | ||||||
|  |     def test_raise_finally(self): | ||||||
|  |         try: | ||||||
|  |             try: | ||||||
|  |                 1/0 | ||||||
|  |             finally: | ||||||
|  |                 raise OSError | ||||||
|  |         except OSError as e: | ||||||
|  |             self.failUnless(isinstance(e.__context__, ZeroDivisionError)) | ||||||
|  |         else: | ||||||
|  |             self.fail("No exception raised") | ||||||
|  | 
 | ||||||
|  |     def test_context_manager(self): | ||||||
|  |         class ContextManager: | ||||||
|  |             def __enter__(self): | ||||||
|  |                 pass | ||||||
|  |             def __exit__(self, t, v, tb): | ||||||
|  |                 xyzzy | ||||||
|  |         try: | ||||||
|  |             with ContextManager(): | ||||||
|  |                 1/0 | ||||||
|  |         except NameError as e: | ||||||
|  |             self.failUnless(isinstance(e.__context__, ZeroDivisionError)) | ||||||
|  |         else: | ||||||
|  |             self.fail("No exception raised") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TestRemovedFunctionality(unittest.TestCase): | class TestRemovedFunctionality(unittest.TestCase): | ||||||
|  |  | ||||||
|  | @ -2817,11 +2817,12 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals, | ||||||
| static enum why_code | static enum why_code | ||||||
| do_raise(PyObject *exc, PyObject *cause) | do_raise(PyObject *exc, PyObject *cause) | ||||||
| { | { | ||||||
| 	PyObject *type = NULL, *value = NULL, *tb = NULL; | 	PyObject *type = NULL, *value = NULL; | ||||||
| 
 | 
 | ||||||
| 	if (exc == NULL) { | 	if (exc == NULL) { | ||||||
| 		/* Reraise */ | 		/* Reraise */ | ||||||
| 		PyThreadState *tstate = PyThreadState_GET(); | 		PyThreadState *tstate = PyThreadState_GET(); | ||||||
|  | 		PyObject *tb; | ||||||
| 		type = tstate->exc_type; | 		type = tstate->exc_type; | ||||||
| 		value = tstate->exc_value; | 		value = tstate->exc_value; | ||||||
| 		tb = tstate->exc_traceback; | 		tb = tstate->exc_traceback; | ||||||
|  | @ -2862,7 +2863,6 @@ do_raise(PyObject *exc, PyObject *cause) | ||||||
| 		goto raise_error; | 		goto raise_error; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	tb = PyException_GetTraceback(value); |  | ||||||
| 	if (cause) { | 	if (cause) { | ||||||
| 		PyObject *fixed_cause; | 		PyObject *fixed_cause; | ||||||
| 		if (PyExceptionClass_Check(cause)) { | 		if (PyExceptionClass_Check(cause)) { | ||||||
|  | @ -2883,13 +2883,15 @@ do_raise(PyObject *exc, PyObject *cause) | ||||||
| 		PyException_SetCause(value, fixed_cause); | 		PyException_SetCause(value, fixed_cause); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	PyErr_Restore(type, value, tb); | 	PyErr_SetObject(type, value); | ||||||
|  | 	/* PyErr_SetObject incref's its arguments */ | ||||||
|  | 	Py_XDECREF(value); | ||||||
|  | 	Py_XDECREF(type); | ||||||
| 	return WHY_EXCEPTION; | 	return WHY_EXCEPTION; | ||||||
| 
 | 
 | ||||||
| raise_error: | raise_error: | ||||||
| 	Py_XDECREF(value); | 	Py_XDECREF(value); | ||||||
| 	Py_XDECREF(type); | 	Py_XDECREF(type); | ||||||
| 	Py_XDECREF(tb); |  | ||||||
| 	Py_XDECREF(cause); | 	Py_XDECREF(cause); | ||||||
| 	return WHY_EXCEPTION; | 	return WHY_EXCEPTION; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -52,6 +52,9 @@ PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback) | ||||||
| void | void | ||||||
| PyErr_SetObject(PyObject *exception, PyObject *value) | PyErr_SetObject(PyObject *exception, PyObject *value) | ||||||
| { | { | ||||||
|  | 	PyThreadState *tstate = PyThreadState_GET(); | ||||||
|  | 	PyObject *tb = NULL; | ||||||
|  | 
 | ||||||
| 	if (exception != NULL && | 	if (exception != NULL && | ||||||
| 	    !PyExceptionClass_Check(exception)) { | 	    !PyExceptionClass_Check(exception)) { | ||||||
| 		PyErr_Format(PyExc_SystemError, | 		PyErr_Format(PyExc_SystemError, | ||||||
|  | @ -59,9 +62,35 @@ PyErr_SetObject(PyObject *exception, PyObject *value) | ||||||
| 			     exception); | 			     exception); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 	Py_XINCREF(exception); |  | ||||||
| 	Py_XINCREF(value); | 	Py_XINCREF(value); | ||||||
| 	PyErr_Restore(exception, value, (PyObject *)NULL); | 	if (tstate->exc_value != NULL && tstate->exc_value != Py_None) { | ||||||
|  | 		/* Implicit exception chaining */ | ||||||
|  | 		if (value == NULL || !PyExceptionInstance_Check(value)) { | ||||||
|  | 			/* We must normalize the value right now */ | ||||||
|  | 			PyObject *args, *fixed_value; | ||||||
|  | 			if (value == NULL || value == Py_None) | ||||||
|  | 				args = PyTuple_New(0); | ||||||
|  | 			else if (PyTuple_Check(value)) { | ||||||
|  | 				Py_INCREF(value); | ||||||
|  | 				args = value; | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 				args = PyTuple_Pack(1, value); | ||||||
|  | 			fixed_value = args ? | ||||||
|  | 				PyEval_CallObject(exception, args) : NULL; | ||||||
|  | 			Py_XDECREF(args); | ||||||
|  | 			Py_XDECREF(value); | ||||||
|  | 			if (fixed_value == NULL) | ||||||
|  | 				return; | ||||||
|  | 			value = fixed_value; | ||||||
|  | 		} | ||||||
|  | 		Py_INCREF(tstate->exc_value); | ||||||
|  | 		PyException_SetContext(value, tstate->exc_value); | ||||||
|  | 	} | ||||||
|  | 	if (value != NULL && PyExceptionInstance_Check(value)) | ||||||
|  | 		tb = PyException_GetTraceback(value); | ||||||
|  | 	Py_XINCREF(exception); | ||||||
|  | 	PyErr_Restore(exception, value, tb); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Guido van Rossum
						Guido van Rossum