@@ -22,6 +22,158 @@ 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+ def test_undefinedExceptionNameObscuringLocalVariableFalsePositive1 (self ):
69+ """Exception names obscure locals, can't be used after. Unless.
70+
71+ Last line will never raise UnboundLocalError because it's only
72+ entered if no exception was raised."""
73+ self .flakes ('''
74+ exc = 'Original value'
75+ try:
76+ raise ValueError('ve')
77+ except ValueError as exc:
78+ print('exception logged')
79+ raise
80+ exc
81+ ''' )
82+
83+ def test_undefinedExceptionNameObscuringLocalVariableFalsePositive2 (self ):
84+ """Exception names obscure locals, can't be used after. Unless.
85+
86+ Last line will never raise UnboundLocalError because `error` is
87+ only falsy if the `except:` block has not been entered."""
88+ self .flakes ('''
89+ exc = 'Original value'
90+ error = None
91+ try:
92+ raise ValueError('ve')
93+ except ValueError as exc:
94+ error = 'exception logged'
95+ if error:
96+ print(error)
97+ else:
98+ exc
99+ ''' )
100+
101+ @skip ('error reporting disabled due to false positives below' )
102+ def test_undefinedExceptionNameObscuringGlobalVariable (self ):
103+ """Exception names obscure globals, can't be used after.
104+
105+ Last line will raise UnboundLocalError on both Python 2 and
106+ Python 3 because the existence of that exception name creates
107+ a local scope placeholder for it, obscuring any globals, etc."""
108+ self .flakes ('''
109+ exc = 'Original value'
110+ def func():
111+ try:
112+ pass # nothing is raised
113+ except ValueError as exc:
114+ pass # block never entered, exc stays unbound
115+ exc
116+ ''' ,
117+ m .UndefinedLocal )
118+
119+ @skip ('error reporting disabled due to false positives below' )
120+ def test_undefinedExceptionNameObscuringGlobalVariable2 (self ):
121+ """Exception names obscure globals, can't be used after.
122+
123+ Last line will raise NameError on Python 3 because the name is
124+ locally unbound after the `except:` block, even if it's
125+ nonlocal. We should issue an error in this case because code
126+ only working correctly if an exception isn't raised, is invalid.
127+ Unless it's explicitly silenced, see false positives below."""
128+ self .flakes ('''
129+ exc = 'Original value'
130+ def func():
131+ global exc
132+ try:
133+ raise ValueError('ve')
134+ except ValueError as exc:
135+ pass # block never entered, exc stays unbound
136+ exc
137+ ''' ,
138+ m .UndefinedLocal )
139+
140+ def test_undefinedExceptionNameObscuringGlobalVariableFalsePositive1 (self ):
141+ """Exception names obscure globals, can't be used after. Unless.
142+
143+ Last line will never raise NameError because it's only entered
144+ if no exception was raised."""
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+ print('exception logged')
153+ raise
154+ exc
155+ ''' )
156+
157+ def test_undefinedExceptionNameObscuringGlobalVariableFalsePositive2 (self ):
158+ """Exception names obscure globals, can't be used after. Unless.
159+
160+ Last line will never raise NameError because `error` is only
161+ falsy if the `except:` block has not been entered."""
162+ self .flakes ('''
163+ exc = 'Original value'
164+ def func():
165+ global exc
166+ error = None
167+ try:
168+ raise ValueError('ve')
169+ except ValueError as exc:
170+ error = 'exception logged'
171+ if error:
172+ print(error)
173+ else:
174+ exc
175+ ''' )
176+
25177 def test_functionsNeedGlobalScope (self ):
26178 self .flakes ('''
27179 class a:
0 commit comments