zmc
2023-12-22 9fdbf60165db0400c2e8e6be2dc6e88138ac719a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
from __future__ import print_function
from __future__ import absolute_import
 
import signal
from multiprocessing import Process
 
import greenlet
from . import _test_extension_cpp
from . import TestCase
 
def run_unhandled_exception_in_greenlet_aborts():
    # This is used in multiprocessing.Process and must be picklable
    # so it needs to be global.
 
 
    def _():
        _test_extension_cpp.test_exception_switch_and_do_in_g2(
            _test_extension_cpp.test_exception_throw
        )
    g1 = greenlet.greenlet(_)
    g1.switch()
 
class CPPTests(TestCase):
    def test_exception_switch(self):
        greenlets = []
        for i in range(4):
            g = greenlet.greenlet(_test_extension_cpp.test_exception_switch)
            g.switch(i)
            greenlets.append(g)
        for i, g in enumerate(greenlets):
            self.assertEqual(g.switch(), i)
 
    def _do_test_unhandled_exception(self, target):
        # TODO: On some versions of Python with some settings, this
        # spews a lot of garbage to stderr. It would be nice to capture and ignore that.
        import sys
        WIN = sys.platform.startswith("win")
 
        p = Process(target=target)
        p.start()
        p.join(10)
        # The child should be aborted in an unusual way. On POSIX
        # platforms, this is done with abort() and signal.SIGABRT,
        # which is reflected in a negative return value; however, on
        # Windows, even though we observe the child print "Fatal
        # Python error: Aborted" and in older versions of the C
        # runtime "This application has requested the Runtime to
        # terminate it in an unusual way," it always has an exit code
        # of 3. This is interesting because 3 is the error code for
        # ERROR_PATH_NOT_FOUND; BUT: the C runtime abort() function
        # also uses this code.
        #
        # See
        # https://devblogs.microsoft.com/oldnewthing/20110519-00/?p=10623
        # and
        # https://docs.microsoft.com/en-us/previous-versions/k089yyh0(v=vs.140)?redirectedfrom=MSDN
        expected_exit = (
            -signal.SIGABRT,
            # But beginning on Python 3.11, the faulthandler
            # that prints the C backtraces sometimes segfaults after
            # reporting the exception but before printing the stack.
            # This has only been seen on linux/gcc.
            -signal.SIGSEGV
        ) if not WIN else (
            3,
        )
        self.assertIn(p.exitcode, expected_exit)
 
    def test_unhandled_exception_aborts(self):
        # verify that plain unhandled throw aborts
        self._do_test_unhandled_exception(_test_extension_cpp.test_exception_throw)
 
 
    def test_unhandled_exception_in_greenlet_aborts(self):
        # verify that unhandled throw called in greenlet aborts too
        self._do_test_unhandled_exception(run_unhandled_exception_in_greenlet_aborts)
 
 
if __name__ == '__main__':
    __import__('unittest').main()