bstpierre (owner)

Revisions

gist: 148135 Download_button fork
public
Description:
Python Exception Handling: Cleanup and Reraise
Public Clone URL: git://gist.github.com/148135.git
Embed All Files: show embed
cleanup_and_reraise.py #
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#!/usr/bin/python
#
# This code sample shows (what I hope to be) the right way to reraise an exception in the
# situation where you have to perform some cleanup before reraising.
#
 
cleaned_up = False
 
def raiser():
    raise RuntimeError("this should be reported at line 10")
 
def cleanup_raises():
    global cleaned_up
    cleaned_up = True
    raise RuntimeError("you should not see this!")
 
def reraiser1():
    try:
        raiser()
    except RuntimeError, e:
        try:
            cleanup_raises()
        except RuntimeError:
            pass
        # Wrong: stack trace reports this as the original scene of the
        # crime. Location information should say line 10 but it doesn't
        raise e
 
def reraiser2():
    try:
        raiser()
    except RuntimeError, e:
        try:
            cleanup_raises()
        except RuntimeError:
            pass
        # Wrong: stack trace reports cleanup_raises as the culprit. We
        # want to know about raiser.
        raise
 
def reraiser3():
    try:
        raiser()
    except RuntimeError, e:
        def cleanup():
            try:
                cleanup_raises()
            except RuntimeError:
                pass
        cleanup()
        # Right: Need to make sure that exceptions from cleanup_raises
        # are handled in a separate scope. This reports the original
        # exception location of raiser at line 10.
        raise
 
def reraiser4():
    try:
        raiser()
    except RuntimeError, e:
        (_, _, traceback) = sys.exc_info()
 
        try:
            cleanup_raises()
        except RuntimeError:
            pass
        # Right: Raise exception but provide original traceback
        # info. (Note: must call sys.exc_info() before calling any
        # other possible raiser.) This also reports raiser at line 10.
        raise e, None, traceback
 
def reraiser5():
    try:
        raiser()
    finally:
        try:
            cleanup_raises()
        except RuntimeError:
            pass
        # Right: Don't even catch the original exception -- just clean
        # up on the way out.
        assert(cleaned_up)
 
import sys
variant = sys.argv[1]
if variant == '1':
    reraiser1()
elif variant == '2':
    reraiser2()
elif variant == '3':
    reraiser3()
elif variant == '4':
    reraiser4()
elif variant == '5':
    reraiser5()