| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 027adf4 commit feec49c
29 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -400,8 +400,61 @@ Querying the error indicator | |||
| 400 | 400 | recursively in subtuples) are searched for a match. | |
| 401 | 401 | ||
| 402 | 402 | ||
| 403 | + .. c:function:: PyObject *PyErr_GetRaisedException(void) | ||
| 404 | + | ||
| 405 | + Returns the exception currently being raised, clearing the exception at | ||
| 406 | + the same time. Do not confuse this with the exception currently being | ||
| 407 | + handled which can be accessed with :c:func:`PyErr_GetHandledException`. | ||
| 408 | + | ||
| 409 | + .. note:: | ||
| 410 | + | ||
| 411 | + This function is normally only used by code that needs to catch exceptions or | ||
| 412 | + by code that needs to save and restore the error indicator temporarily, e.g.:: | ||
| 413 | + | ||
| 414 | + { | ||
| 415 | + PyObject *exc = PyErr_GetRaisedException(); | ||
| 416 | + | ||
| 417 | + /* ... code that might produce other errors ... */ | ||
| 418 | + | ||
| 419 | + PyErr_SetRaisedException(exc); | ||
| 420 | + } | ||
| 421 | + | ||
| 422 | + .. versionadded:: 3.12 | ||
| 423 | + | ||
| 424 | + | ||
| 425 | + .. c:function:: void PyErr_SetRaisedException(PyObject *exc) | ||
| 426 | + | ||
| 427 | + Sets the exception currently being raised ``exc``. | ||
| 428 | + If the exception is already set, it is cleared first. | ||
| 429 | + | ||
| 430 | + ``exc`` must be a valid exception. | ||
| 431 | + (Violating this rules will cause subtle problems later.) | ||
| 432 | + This call consumes a reference to the ``exc`` object: you must own a | ||
| 433 | + reference to that object before the call and after the call you no longer own | ||
| 434 | + that reference. | ||
| 435 | + (If you don't understand this, don't use this function. I warned you.) | ||
| 436 | + | ||
| 437 | + .. note:: | ||
| 438 | + | ||
| 439 | + This function is normally only used by code that needs to save and restore the | ||
| 440 | + error indicator temporarily. Use :c:func:`PyErr_GetRaisedException` to save | ||
| 441 | + the current exception, e.g.:: | ||
| 442 | + | ||
| 443 | + { | ||
| 444 | + PyObject *exc = PyErr_GetRaisedException(); | ||
| 445 | + | ||
| 446 | + /* ... code that might produce other errors ... */ | ||
| 447 | + | ||
| 448 | + PyErr_SetRaisedException(exc); | ||
| 449 | + } | ||
| 450 | + | ||
| 451 | + .. versionadded:: 3.12 | ||
| 452 | + | ||
| 453 | + | ||
| 403 | 454 | .. c:function:: void PyErr_Fetch(PyObject **ptype, PyObject **pvalue, PyObject **ptraceback) | |
| 404 | 455 | ||
| 456 | + As of 3.12, this function is deprecated. Use :c:func:`PyErr_GetRaisedException` instead. | ||
| 457 | + | ||
| 405 | 458 | Retrieve the error indicator into three variables whose addresses are passed. | |
| 406 | 459 | If the error indicator is not set, set all three variables to ``NULL``. If it is | |
| 407 | 460 | set, it will be cleared and you own a reference to each object retrieved. The | |
@@ -421,10 +474,14 @@ Querying the error indicator | |||
| 421 | 474 | PyErr_Restore(type, value, traceback); | |
| 422 | 475 | } | |
| 423 | 476 | ||
| 477 | + .. deprecated:: 3.12 | ||
| 478 | + | ||
| 424 | 479 | ||
| 425 | 480 | .. c:function:: void PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback) | |
| 426 | 481 | ||
| 427 | - Set the error indicator from the three objects. If the error indicator is | ||
| 482 | + As of 3.12, this function is deprecated. Use :c:func:`PyErr_SetRaisedException` instead. | ||
| 483 | + | ||
| 484 | + Set the error indicator from the three objects. If the error indicator is | ||
| 428 | 485 | already set, it is cleared first. If the objects are ``NULL``, the error | |
| 429 | 486 | indicator is cleared. Do not pass a ``NULL`` type and non-``NULL`` value or | |
| 430 | 487 | traceback. The exception type should be a class. Do not pass an invalid | |
@@ -440,9 +497,15 @@ Querying the error indicator | |||
| 440 | 497 | error indicator temporarily. Use :c:func:`PyErr_Fetch` to save the current | |
| 441 | 498 | error indicator. | |
| 442 | 499 | ||
| 500 | + .. deprecated:: 3.12 | ||
| 501 | + | ||
| 443 | 502 | ||
| 444 | 503 | .. c:function:: void PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) | |
| 445 | 504 | ||
| 505 | + As of 3.12, this function is deprecated. | ||
| 506 | + Use :c:func:`PyErr_GetRaisedException` instead of :c:func:`PyErr_Fetch` to avoid | ||
| 507 | + any possible de-normalization. | ||
| 508 | + | ||
| 446 | 509 | Under certain circumstances, the values returned by :c:func:`PyErr_Fetch` below | |
| 447 | 510 | can be "unnormalized", meaning that ``*exc`` is a class object but ``*val`` is | |
| 448 | 511 | not an instance of the same class. This function can be used to instantiate | |
@@ -459,6 +522,8 @@ Querying the error indicator | |||
| 459 | 522 | PyException_SetTraceback(val, tb); | |
| 460 | 523 | } | |
| 461 | 524 | ||
| 525 | + .. deprecated:: 3.12 | ||
| 526 | + | ||
| 462 | 527 | ||
| 463 | 528 | .. c:function:: PyObject* PyErr_GetHandledException(void) | |
| 464 | 529 | ||
@@ -704,6 +769,18 @@ Exception Objects | |||
| 704 | 769 | :attr:`__suppress_context__` is implicitly set to ``True`` by this function. | |
| 705 | 770 | ||
| 706 | 771 | ||
| 772 | + .. c:function:: PyObject* PyException_GetArgs(PyObject *ex) | ||
| 773 | + | ||
| 774 | + Return args of the given exception as a new reference, | ||
| 775 | + as accessible from Python through :attr:`args`. | ||
| 776 | + | ||
| 777 | + | ||
| 778 | + .. c:function:: void PyException_SetArgs(PyObject *ex, PyObject *args) | ||
| 779 | + | ||
| 780 | + Set the args of the given exception, | ||
| 781 | + as accessible from Python through :attr:`args`. | ||
| 782 | + | ||
| 783 | + | ||
| 707 | 784 | .. _unicodeexceptions: | |
| 708 | 785 | ||
| 709 | 786 | Unicode Exception Objects | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -99,6 +99,7 @@ PyAPI_FUNC(void) _PyErr_GetExcInfo(PyThreadState *, PyObject **, PyObject **, Py | |||
| 99 | 99 | /* Context manipulation (PEP 3134) */ | |
| 100 | 100 | ||
| 101 | 101 | PyAPI_FUNC(void) _PyErr_ChainExceptions(PyObject *, PyObject *, PyObject *); | |
| 102 | + PyAPI_FUNC(void) _PyErr_ChainExceptions1(PyObject *); | ||
| 102 | 103 | ||
| 103 | 104 | /* Like PyErr_Format(), but saves current exception as __context__ and | |
| 104 | 105 | __cause__. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -166,9 +166,7 @@ struct _ts { | |||
| 166 | 166 | PyObject *c_traceobj; | |
| 167 | 167 | ||
| 168 | 168 | /* The exception currently being raised */ | |
| 169 | - PyObject *curexc_type; | ||
| 170 | - PyObject *curexc_value; | ||
| 171 | - PyObject *curexc_traceback; | ||
| 169 | + PyObject *current_exception; | ||
| 172 | 170 | ||
| 173 | 171 | /* Pointer to the top of the exception stack for the exceptions | |
| 174 | 172 | * we may be currently handling. (See _PyErr_StackItem above.) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -20,7 +20,10 @@ extern void _PyErr_FiniTypes(PyInterpreterState *); | |||
| 20 | 20 | static inline PyObject* _PyErr_Occurred(PyThreadState *tstate) | |
| 21 | 21 | { | |
| 22 | 22 | assert(tstate != NULL); | |
| 23 | - return tstate->curexc_type; | ||
| 23 | + if (tstate->current_exception == NULL) { | ||
| 24 | + return NULL; | ||
| 25 | + } | ||
| 26 | + return (PyObject *)Py_TYPE(tstate->current_exception); | ||
| 24 | 27 | } | |
| 25 | 28 | ||
| 26 | 29 | static inline void _PyErr_ClearExcState(_PyErr_StackItem *exc_state) | |
@@ -37,10 +40,16 @@ PyAPI_FUNC(void) _PyErr_Fetch( | |||
| 37 | 40 | PyObject **value, | |
| 38 | 41 | PyObject **traceback); | |
| 39 | 42 | ||
| 43 | + extern PyObject * | ||
| 44 | + _PyErr_GetRaisedException(PyThreadState *tstate); | ||
| 45 | + | ||
| 40 | 46 | PyAPI_FUNC(int) _PyErr_ExceptionMatches( | |
| 41 | 47 | PyThreadState *tstate, | |
| 42 | 48 | PyObject *exc); | |
| 43 | 49 | ||
| 50 | + void | ||
| 51 | + _PyErr_SetRaisedException(PyThreadState *tstate, PyObject *exc); | ||
| 52 | + | ||
| 44 | 53 | PyAPI_FUNC(void) _PyErr_Restore( | |
| 45 | 54 | PyThreadState *tstate, | |
| 46 | 55 | PyObject *type, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,6 +18,8 @@ PyAPI_FUNC(PyObject *) PyErr_Occurred(void); | |||
| 18 | 18 | PyAPI_FUNC(void) PyErr_Clear(void); | |
| 19 | 19 | PyAPI_FUNC(void) PyErr_Fetch(PyObject **, PyObject **, PyObject **); | |
| 20 | 20 | PyAPI_FUNC(void) PyErr_Restore(PyObject *, PyObject *, PyObject *); | |
| 21 | + PyAPI_FUNC(PyObject *) PyErr_GetRaisedException(void); | ||
| 22 | + PyAPI_FUNC(void) PyErr_SetRaisedException(PyObject *); | ||
| 21 | 23 | #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030b0000 | |
| 22 | 24 | PyAPI_FUNC(PyObject*) PyErr_GetHandledException(void); | |
| 23 | 25 | PyAPI_FUNC(void) PyErr_SetHandledException(PyObject *); | |
@@ -51,6 +53,10 @@ PyAPI_FUNC(void) PyException_SetCause(PyObject *, PyObject *); | |||
| 51 | 53 | PyAPI_FUNC(PyObject *) PyException_GetContext(PyObject *); | |
| 52 | 54 | PyAPI_FUNC(void) PyException_SetContext(PyObject *, PyObject *); | |
| 53 | 55 | ||
| 56 | + | ||
| 57 | + PyAPI_FUNC(PyObject *) PyException_GetArgs(PyObject *); | ||
| 58 | + PyAPI_FUNC(void) PyException_SetArgs(PyObject *, PyObject *); | ||
| 59 | + | ||
| 54 | 60 | /* */ | |
| 55 | 61 | ||
| 56 | 62 | #define PyExceptionClass_Check(x) \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1553,5 +1553,44 @@ def func2(x=None): | |||
| 1553 | 1553 | self.do_test(func2) | |
| 1554 | 1554 | ||
| 1555 | 1555 | ||
| 1556 | + class Test_ErrSetAndRestore(unittest.TestCase): | ||
| 1557 | + | ||
| 1558 | + def test_err_set_raised(self): | ||
| 1559 | + with self.assertRaises(ValueError): | ||
| 1560 | + _testcapi.err_set_raised(ValueError()) | ||
| 1561 | + v = ValueError() | ||
| 1562 | + try: | ||
| 1563 | + _testcapi.err_set_raised(v) | ||
| 1564 | + except ValueError as ex: | ||
| 1565 | + self.assertIs(v, ex) | ||
| 1566 | + | ||
| 1567 | + def test_err_restore(self): | ||
| 1568 | + with self.assertRaises(ValueError): | ||
| 1569 | + _testcapi.err_restore(ValueError) | ||
| 1570 | + with self.assertRaises(ValueError): | ||
| 1571 | + _testcapi.err_restore(ValueError, 1) | ||
| 1572 | + with self.assertRaises(ValueError): | ||
| 1573 | + _testcapi.err_restore(ValueError, 1, None) | ||
| 1574 | + with self.assertRaises(ValueError): | ||
| 1575 | + _testcapi.err_restore(ValueError, ValueError()) | ||
| 1576 | + try: | ||
| 1577 | + _testcapi.err_restore(KeyError, "hi") | ||
| 1578 | + except KeyError as k: | ||
| 1579 | + self.assertEqual("hi", k.args[0]) | ||
| 1580 | + try: | ||
| 1581 | + 1/0 | ||
| 1582 | + except Exception as e: | ||
| 1583 | + tb = e.__traceback__ | ||
| 1584 | + with self.assertRaises(ValueError): | ||
| 1585 | + _testcapi.err_restore(ValueError, 1, tb) | ||
| 1586 | + with self.assertRaises(TypeError): | ||
| 1587 | + _testcapi.err_restore(ValueError, 1, 0) | ||
| 1588 | + try: | ||
| 1589 | + _testcapi.err_restore(ValueError, 1, tb) | ||
| 1590 | + except ValueError as v: | ||
| 1591 | + self.assertEqual(1, v.args[0]) | ||
| 1592 | + self.assertIs(tb, v.__traceback__.tb_next) | ||
| 1593 | + | ||
| 1594 | + | ||
| 1556 | 1595 | if __name__ == "__main__": | |
| 1557 | 1596 | unittest.main() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -347,6 +347,7 @@ def test_capi2(): | |||
| 347 | 347 | _testcapi.raise_exception(BadException, 0) | |
| 348 | 348 | except RuntimeError as err: | |
| 349 | 349 | exc, err, tb = sys.exc_info() | |
| 350 | + tb = tb.tb_next | ||
| 350 | 351 | co = tb.tb_frame.f_code | |
| 351 | 352 | self.assertEqual(co.co_name, "__init__") | |
| 352 | 353 | self.assertTrue(co.co_filename.endswith('test_exceptions.py')) | |
@@ -1415,8 +1416,8 @@ def gen(): | |||
| 1415 | 1416 | @cpython_only | |
| 1416 | 1417 | def test_recursion_normalizing_infinite_exception(self): | |
| 1417 | 1418 | # Issue #30697. Test that a RecursionError is raised when | |
| 1418 | - # PyErr_NormalizeException() maximum recursion depth has been | ||
| 1419 | - # exceeded. | ||
| 1419 | + # maximum recursion depth has been exceeded when creating | ||
| 1420 | + # an exception | ||
| 1420 | 1421 | code = """if 1: | |
| 1421 | 1422 | import _testcapi | |
| 1422 | 1423 | try: | |
@@ -1426,8 +1427,7 @@ def test_recursion_normalizing_infinite_exception(self): | |||
| 1426 | 1427 | """ | |
| 1427 | 1428 | rc, out, err = script_helper.assert_python_failure("-c", code) | |
| 1428 | 1429 | self.assertEqual(rc, 1) | |
| 1429 | - self.assertIn(b'RecursionError: maximum recursion depth exceeded ' | ||
| 1430 | - b'while normalizing an exception', err) | ||
| 1430 | + self.assertIn(b'RecursionError: maximum recursion depth exceeded', err) | ||
| 1431 | 1431 | self.assertIn(b'Done.', out) | |
| 1432 | 1432 | ||
| 1433 | 1433 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,13 @@ | |||
| 1 | + Add new C-API functions for saving and restoring the current exception: | ||
| 2 | + ``PyErr_GetRaisedException`` and ``PyErr_SetRaisedException``. | ||
| 3 | + These functions take and return a single exception rather than | ||
| 4 | + the triple of ``PyErr_Fetch`` and ``PyErr_Restore``. | ||
| 5 | + This is less error prone and a bit more efficient. | ||
| 6 | + | ||
| 7 | + The three arguments forms of saving and restoring the | ||
| 8 | + current exception: ``PyErr_Fetch`` and ``PyErr_Restore`` | ||
| 9 | + are deprecated. | ||
| 10 | + | ||
| 11 | + Also add ``PyException_GetArgs`` and ``PyException_SetArgs`` | ||
| 12 | + as convenience functions to help dealing with | ||
| 13 | + exceptions in the C API. | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments