@@ -22,6 +22,175 @@ def test_undefinedInListComp(self):
2222 ''' ,
2323 m .UndefinedName )
2424
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+
25194 def test_functionsNeedGlobalScope (self ):
26195 self .flakes ('''
27196 class a:
0 commit comments