Commit 69f163fb authored by Stefan Behnel's avatar Stefan Behnel

make except-as delete the target after the except clause

parent 48c6bb4c
......@@ -1098,6 +1098,8 @@ class ControlFlowAnalysis(CythonTransform):
if clause.target:
self.mark_assignment(clause.target)
self.visit(clause.body)
if clause.is_except_as:
self.flow.mark_deletion(clause.target, clause.target.entry)
if self.flow.block:
self.flow.block.add_child(next_block)
......
......@@ -6033,6 +6033,7 @@ class ExceptClauseNode(Node):
# exc_value ExcValueNode used internally
# function_name string qualified name of enclosing function
# exc_vars (string * 3) local exception variables
# is_except_as bool Py3-style "except ... as xyz"
# excinfo_target is never set by the parser, but can be set by a transform
# in order to extract more extensive information about the exception as a
......@@ -6042,6 +6043,7 @@ class ExceptClauseNode(Node):
exc_value = None
excinfo_target = None
is_except_as = False
def analyse_declarations(self, env):
if self.target:
......@@ -6089,11 +6091,16 @@ class ExceptClauseNode(Node):
else:
code.putln("/*except:*/ {")
if not getattr(self.body, 'stats', True) and \
self.excinfo_target is None and self.target is None:
if (not getattr(self.body, 'stats', True)
and self.excinfo_target is None
and (self.target is None or self.is_except_as)):
# most simple case: no exception variable, empty body (pass)
# => reset the exception state, done
code.putln("PyErr_Restore(0,0,0);")
if self.is_except_as and self.target:
# "except ... as x" deletes x after use
# target is known to be a NameNode
self.target.generate_deletion_code(code)
code.put_goto(end_label)
code.putln("}")
return
......@@ -6133,6 +6140,10 @@ class ExceptClauseNode(Node):
self.excinfo_tuple.generate_disposal_code(code)
for var in exc_vars:
code.put_decref_clear(var, py_object_type)
if self.is_except_as and self.target:
# "except ... as x" deletes x after use
# target is known to be a NameNode
self.target.generate_deletion_code(code)
code.put_goto(end_label)
if code.label_used(code.break_label):
......@@ -6141,6 +6152,8 @@ class ExceptClauseNode(Node):
self.excinfo_tuple.generate_disposal_code(code)
for var in exc_vars:
code.put_decref_clear(var, py_object_type)
if self.is_except_as and self.target:
self.target.generate_deletion_code(code)
code.put_goto(old_break_label)
code.break_label = old_break_label
......@@ -6150,6 +6163,8 @@ class ExceptClauseNode(Node):
self.excinfo_tuple.generate_disposal_code(code)
for var in exc_vars:
code.put_decref_clear(var, py_object_type)
if self.is_except_as and self.target:
self.target.generate_deletion_code(code)
code.put_goto(old_continue_label)
code.continue_label = old_continue_label
......
......@@ -1565,6 +1565,7 @@ def p_except_clause(s):
s.next()
exc_type = None
exc_value = None
is_except_as = False
if s.sy != ':':
exc_type = p_test(s)
# normalise into list of single exception tests
......@@ -1572,7 +1573,7 @@ def p_except_clause(s):
exc_type = exc_type.args
else:
exc_type = [exc_type]
if s.sy == ',' or (s.sy == 'IDENT' and s.systring == 'as'):
if s.sy == ',':
s.next()
exc_value = p_test(s)
elif s.sy == 'IDENT' and s.systring == 'as':
......@@ -1581,9 +1582,11 @@ def p_except_clause(s):
pos2 = s.position()
name = p_ident(s)
exc_value = ExprNodes.NameNode(pos2, name = name)
is_except_as = True
body = p_suite(s)
return Nodes.ExceptClauseNode(pos,
pattern = exc_type, target = exc_value, body = body)
pattern = exc_type, target = exc_value,
body = body, is_except_as=is_except_as)
def p_include_statement(s, ctx):
pos = s.position()
......
......@@ -13,6 +13,11 @@ __doc__ = u"""
'ValueError'
>>> exc[3] is val
True
>>> except_as_deletes
True
>>> no_match_does_not_delete
True
"""
a = 0
......@@ -48,8 +53,9 @@ except KeyError as e:
except IndexError as e:
exc[0] = e
except:
exc[0] = e
exc[0] = 'SOMETHING ELSE'
e = None
try:
raise KeyError
except AttributeError as e:
......@@ -59,7 +65,23 @@ except KeyError as e:
except IndexError as e:
exc[1] = e
except:
exc[1] = e
exc[1] = 'SOMETHING ELSE'
try:
e
except NameError:
except_as_deletes = True
else:
except_as_deletes = False
e = 123
try:
raise TypeError
except NameError as e:
pass
except TypeError:
pass
no_match_does_not_delete = (e == 123)
try:
raise IndexError
......@@ -70,7 +92,7 @@ except KeyError as e:
except IndexError as e:
exc[2] = e
except:
exc[2] = e
exc[2] = 'SOMETHING ELSE'
val = None
try:
......
......@@ -343,6 +343,47 @@ def except_as_raise(x, a):
assert isinstance(b, a)
return i
def except_as_no_raise_does_not_touch_target(a):
"""
>>> i,b = except_as_no_raise_does_not_touch_target(TypeError)
>>> i
1
>>> b
1
"""
b = 1
try:
i = 1
except a as b:
i = 2
return i, b
def except_as_raise_deletes_target(x, a):
"""
>>> except_as_raise_deletes_target(None, TypeError)
1
1
>>> except_as_raise_deletes_target(TypeError('test'), TypeError)
Traceback (most recent call last):
UnboundLocalError: local variable 'b' referenced before assignment
>>> except_as_raise_deletes_target(ValueError('test'), TypeError)
Traceback (most recent call last):
ValueError: test
>>> except_as_raise_deletes_target(None, TypeError)
1
1
"""
b = 1
try:
i = 1
if x:
raise x
except a as b:
i = 2
assert isinstance(b, a)
print(b) # raises NameError if except clause was executed
return i
def complete_except_as_no_raise(a, b):
"""
>>> complete_except_as_no_raise(TypeError, ValueError)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment