@@ -22,6 +22,175 @@ def test_undefinedInListComp(self):
22
22
''' ,
23
23
m .UndefinedName )
24
24
25
+ @skipIf (version_info < (3 ,),
26
+ 'in Python 2 exception names stay bound after the except: block' )
27
+ def test_undefinedExceptionName (self ):
28
+ """Exception names can't be used after the except: block."""
29
+ self .flakes ('''
30
+ try:
31
+ raise ValueError('ve')
32
+ except ValueError as exc:
33
+ pass
34
+ exc
35
+ ''' ,
36
+ m .UndefinedName )
37
+
38
+ def test_namesDeclaredInExceptBlocks (self ):
39
+ """Locals declared in except: blocks can be used after the block.
40
+
41
+ This shows the example in test_undefinedExceptionName is
42
+ different."""
43
+ self .flakes ('''
44
+ try:
45
+ raise ValueError('ve')
46
+ except ValueError as exc:
47
+ e = exc
48
+ e
49
+ ''' )
50
+
51
+ @skip ('error reporting disabled due to false positives below' )
52
+ def test_undefinedExceptionNameObscuringLocalVariable (self ):
53
+ """Exception names obscure locals, can't be used after.
54
+
55
+ Last line will raise UnboundLocalError on Python 3 after exiting
56
+ the except: block. Note next two examples for false positives to
57
+ watch out for."""
58
+ self .flakes ('''
59
+ exc = 'Original value'
60
+ try:
61
+ raise ValueError('ve')
62
+ except ValueError as exc:
63
+ pass
64
+ exc
65
+ ''' ,
66
+ m .UndefinedName )
67
+
68
+ @skipIf (version_info < (3 ,),
69
+ 'in Python 2 exception names stay bound after the except: block' )
70
+ def test_undefinedExceptionNameObscuringLocalVariable2 (self ):
71
+ """Exception names are unbound after the `except:` block.
72
+
73
+ Last line will raise UnboundLocalError on Python 3 but would print out
74
+ 've' on Python 2."""
75
+ self .flakes ('''
76
+ try:
77
+ raise ValueError('ve')
78
+ except ValueError as exc:
79
+ pass
80
+ print(exc)
81
+ exc = 'Original value'
82
+ ''' ,
83
+ m .UndefinedName )
84
+
85
+ def test_undefinedExceptionNameObscuringLocalVariableFalsePositive1 (self ):
86
+ """Exception names obscure locals, can't be used after. Unless.
87
+
88
+ Last line will never raise UnboundLocalError because it's only
89
+ entered if no exception was raised."""
90
+ self .flakes ('''
91
+ exc = 'Original value'
92
+ try:
93
+ raise ValueError('ve')
94
+ except ValueError as exc:
95
+ print('exception logged')
96
+ raise
97
+ exc
98
+ ''' )
99
+
100
+ def test_undefinedExceptionNameObscuringLocalVariableFalsePositive2 (self ):
101
+ """Exception names obscure locals, can't be used after. Unless.
102
+
103
+ Last line will never raise UnboundLocalError because `error` is
104
+ only falsy if the `except:` block has not been entered."""
105
+ self .flakes ('''
106
+ exc = 'Original value'
107
+ error = None
108
+ try:
109
+ raise ValueError('ve')
110
+ except ValueError as exc:
111
+ error = 'exception logged'
112
+ if error:
113
+ print(error)
114
+ else:
115
+ exc
116
+ ''' )
117
+
118
+ @skip ('error reporting disabled due to false positives below' )
119
+ def test_undefinedExceptionNameObscuringGlobalVariable (self ):
120
+ """Exception names obscure globals, can't be used after.
121
+
122
+ Last line will raise UnboundLocalError on both Python 2 and
123
+ Python 3 because the existence of that exception name creates
124
+ a local scope placeholder for it, obscuring any globals, etc."""
125
+ self .flakes ('''
126
+ exc = 'Original value'
127
+ def func():
128
+ try:
129
+ pass # nothing is raised
130
+ except ValueError as exc:
131
+ pass # block never entered, exc stays unbound
132
+ exc
133
+ ''' ,
134
+ m .UndefinedLocal )
135
+
136
+ @skip ('error reporting disabled due to false positives below' )
137
+ def test_undefinedExceptionNameObscuringGlobalVariable2 (self ):
138
+ """Exception names obscure globals, can't be used after.
139
+
140
+ Last line will raise NameError on Python 3 because the name is
141
+ locally unbound after the `except:` block, even if it's
142
+ nonlocal. We should issue an error in this case because code
143
+ only working correctly if an exception isn't raised, is invalid.
144
+ Unless it's explicitly silenced, see false positives below."""
145
+ self .flakes ('''
146
+ exc = 'Original value'
147
+ def func():
148
+ global exc
149
+ try:
150
+ raise ValueError('ve')
151
+ except ValueError as exc:
152
+ pass # block never entered, exc stays unbound
153
+ exc
154
+ ''' ,
155
+ m .UndefinedLocal )
156
+
157
+ def test_undefinedExceptionNameObscuringGlobalVariableFalsePositive1 (self ):
158
+ """Exception names obscure globals, can't be used after. Unless.
159
+
160
+ Last line will never raise NameError because it's only entered
161
+ if no exception was raised."""
162
+ self .flakes ('''
163
+ exc = 'Original value'
164
+ def func():
165
+ global exc
166
+ try:
167
+ raise ValueError('ve')
168
+ except ValueError as exc:
169
+ print('exception logged')
170
+ raise
171
+ exc
172
+ ''' )
173
+
174
+ def test_undefinedExceptionNameObscuringGlobalVariableFalsePositive2 (self ):
175
+ """Exception names obscure globals, can't be used after. Unless.
176
+
177
+ Last line will never raise NameError because `error` is only
178
+ falsy if the `except:` block has not been entered."""
179
+ self .flakes ('''
180
+ exc = 'Original value'
181
+ def func():
182
+ global exc
183
+ error = None
184
+ try:
185
+ raise ValueError('ve')
186
+ except ValueError as exc:
187
+ error = 'exception logged'
188
+ if error:
189
+ print(error)
190
+ else:
191
+ exc
192
+ ''' )
193
+
25
194
def test_functionsNeedGlobalScope (self ):
26
195
self .flakes ('''
27
196
class a:
0 commit comments