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): ...@@ -1098,6 +1098,8 @@ class ControlFlowAnalysis(CythonTransform):
if clause.target: if clause.target:
self.mark_assignment(clause.target) self.mark_assignment(clause.target)
self.visit(clause.body) self.visit(clause.body)
if clause.is_except_as:
self.flow.mark_deletion(clause.target, clause.target.entry)
if self.flow.block: if self.flow.block:
self.flow.block.add_child(next_block) self.flow.block.add_child(next_block)
......
...@@ -6033,6 +6033,7 @@ class ExceptClauseNode(Node): ...@@ -6033,6 +6033,7 @@ class ExceptClauseNode(Node):
# exc_value ExcValueNode used internally # exc_value ExcValueNode used internally
# function_name string qualified name of enclosing function # function_name string qualified name of enclosing function
# exc_vars (string * 3) local exception variables # 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 # 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 # in order to extract more extensive information about the exception as a
...@@ -6042,6 +6043,7 @@ class ExceptClauseNode(Node): ...@@ -6042,6 +6043,7 @@ class ExceptClauseNode(Node):
exc_value = None exc_value = None
excinfo_target = None excinfo_target = None
is_except_as = False
def analyse_declarations(self, env): def analyse_declarations(self, env):
if self.target: if self.target:
...@@ -6089,11 +6091,16 @@ class ExceptClauseNode(Node): ...@@ -6089,11 +6091,16 @@ class ExceptClauseNode(Node):
else: else:
code.putln("/*except:*/ {") code.putln("/*except:*/ {")
if not getattr(self.body, 'stats', True) and \ if (not getattr(self.body, 'stats', True)
self.excinfo_target is None and self.target is None: 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) # most simple case: no exception variable, empty body (pass)
# => reset the exception state, done # => reset the exception state, done
code.putln("PyErr_Restore(0,0,0);") 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.put_goto(end_label)
code.putln("}") code.putln("}")
return return
...@@ -6133,6 +6140,10 @@ class ExceptClauseNode(Node): ...@@ -6133,6 +6140,10 @@ class ExceptClauseNode(Node):
self.excinfo_tuple.generate_disposal_code(code) self.excinfo_tuple.generate_disposal_code(code)
for var in exc_vars: for var in exc_vars:
code.put_decref_clear(var, py_object_type) 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) code.put_goto(end_label)
if code.label_used(code.break_label): if code.label_used(code.break_label):
...@@ -6141,6 +6152,8 @@ class ExceptClauseNode(Node): ...@@ -6141,6 +6152,8 @@ class ExceptClauseNode(Node):
self.excinfo_tuple.generate_disposal_code(code) self.excinfo_tuple.generate_disposal_code(code)
for var in exc_vars: for var in exc_vars:
code.put_decref_clear(var, py_object_type) 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.put_goto(old_break_label)
code.break_label = old_break_label code.break_label = old_break_label
...@@ -6150,6 +6163,8 @@ class ExceptClauseNode(Node): ...@@ -6150,6 +6163,8 @@ class ExceptClauseNode(Node):
self.excinfo_tuple.generate_disposal_code(code) self.excinfo_tuple.generate_disposal_code(code)
for var in exc_vars: for var in exc_vars:
code.put_decref_clear(var, py_object_type) 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.put_goto(old_continue_label)
code.continue_label = old_continue_label code.continue_label = old_continue_label
......
...@@ -1565,6 +1565,7 @@ def p_except_clause(s): ...@@ -1565,6 +1565,7 @@ def p_except_clause(s):
s.next() s.next()
exc_type = None exc_type = None
exc_value = None exc_value = None
is_except_as = False
if s.sy != ':': if s.sy != ':':
exc_type = p_test(s) exc_type = p_test(s)
# normalise into list of single exception tests # normalise into list of single exception tests
...@@ -1572,7 +1573,7 @@ def p_except_clause(s): ...@@ -1572,7 +1573,7 @@ def p_except_clause(s):
exc_type = exc_type.args exc_type = exc_type.args
else: else:
exc_type = [exc_type] exc_type = [exc_type]
if s.sy == ',' or (s.sy == 'IDENT' and s.systring == 'as'): if s.sy == ',':
s.next() s.next()
exc_value = p_test(s) exc_value = p_test(s)
elif s.sy == 'IDENT' and s.systring == 'as': elif s.sy == 'IDENT' and s.systring == 'as':
...@@ -1581,9 +1582,11 @@ def p_except_clause(s): ...@@ -1581,9 +1582,11 @@ def p_except_clause(s):
pos2 = s.position() pos2 = s.position()
name = p_ident(s) name = p_ident(s)
exc_value = ExprNodes.NameNode(pos2, name = name) exc_value = ExprNodes.NameNode(pos2, name = name)
is_except_as = True
body = p_suite(s) body = p_suite(s)
return Nodes.ExceptClauseNode(pos, 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): def p_include_statement(s, ctx):
pos = s.position() pos = s.position()
......
...@@ -13,6 +13,11 @@ __doc__ = u""" ...@@ -13,6 +13,11 @@ __doc__ = u"""
'ValueError' 'ValueError'
>>> exc[3] is val >>> exc[3] is val
True True
>>> except_as_deletes
True
>>> no_match_does_not_delete
True
""" """
a = 0 a = 0
...@@ -48,8 +53,9 @@ except KeyError as e: ...@@ -48,8 +53,9 @@ except KeyError as e:
except IndexError as e: except IndexError as e:
exc[0] = e exc[0] = e
except: except:
exc[0] = e exc[0] = 'SOMETHING ELSE'
e = None
try: try:
raise KeyError raise KeyError
except AttributeError as e: except AttributeError as e:
...@@ -59,7 +65,23 @@ except KeyError as e: ...@@ -59,7 +65,23 @@ except KeyError as e:
except IndexError as e: except IndexError as e:
exc[1] = e exc[1] = e
except: 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: try:
raise IndexError raise IndexError
...@@ -70,7 +92,7 @@ except KeyError as e: ...@@ -70,7 +92,7 @@ except KeyError as e:
except IndexError as e: except IndexError as e:
exc[2] = e exc[2] = e
except: except:
exc[2] = e exc[2] = 'SOMETHING ELSE'
val = None val = None
try: try:
......
...@@ -343,6 +343,47 @@ def except_as_raise(x, a): ...@@ -343,6 +343,47 @@ def except_as_raise(x, a):
assert isinstance(b, a) assert isinstance(b, a)
return i 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): def complete_except_as_no_raise(a, b):
""" """
>>> complete_except_as_no_raise(TypeError, ValueError) >>> 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