summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorAmaury Forgeot d'Arc <amauryfa@gmail.com>2010-09-10 21:39:53 (GMT)
committerAmaury Forgeot d'Arc <amauryfa@gmail.com>2010-09-10 21:39:53 (GMT)
commitba117ef7e9b2f7f2fbec62a9fcfe371a68024769 (patch)
tree3ca491174a99860c368d8d243d8db31b13b6ecf8 /Lib
parent4785916d62aeb97692bab08bb9e77952853084a4 (diff)
downloadcpython-ba117ef7e9b2f7f2fbec62a9fcfe371a68024769.zip
cpython-ba117ef7e9b2f7f2fbec62a9fcfe371a68024769.tar.gz
cpython-ba117ef7e9b2f7f2fbec62a9fcfe371a68024769.tar.bz2
#4617: Previously it was illegal to delete a name from the local
namespace if it occurs as a free variable in a nested block. This limitation of the compiler has been lifted, and a new opcode introduced (DELETE_DEREF). This sample was valid in 2.6, but fails to compile in 3.x without this change:: >>> def f(): ... def print_error(): ... print(e) ... try: ... something ... except Exception as e: ... print_error() ... # implicit "del e" here This sample has always been invalid in Python, and now works:: >>> def outer(x): ... def inner(): ... return x ... inner() ... del x There is no need to bump the PYC magic number: the new opcode is used for code that did not compile before.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/opcode.py2
-rw-r--r--Lib/test/test_exceptions.py11
-rw-r--r--Lib/test/test_scope.py29
-rw-r--r--Lib/test/test_syntax.py9
4 files changed, 35 insertions, 16 deletions
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 9833b6c..8e15d13 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -161,6 +161,8 @@ def_op('LOAD_DEREF', 136)
hasfree.append(136)
def_op('STORE_DEREF', 137)
hasfree.append(137)
+def_op('DELETE_DEREF', 138)
+hasfree.append(138)
def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8)
def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8)
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index 126a57f..e2824b2 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -526,6 +526,17 @@ class ExceptionTests(unittest.TestCase):
obj = wr()
self.assertTrue(obj is None, "%s" % obj)
+ def test_exception_target_in_nested_scope(self):
+ # issue 4617: This used to raise a SyntaxError
+ # "can not delete variable 'e' referenced in nested scope"
+ def print_error():
+ e
+ try:
+ something
+ except Exception as e:
+ print_error()
+ # implicit "del e" here
+
def test_generator_leaking(self):
# Test that generator exception state doesn't leak into the calling
# frame
diff --git a/Lib/test/test_scope.py b/Lib/test/test_scope.py
index 2ac34cf..0e5a8e9 100644
--- a/Lib/test/test_scope.py
+++ b/Lib/test/test_scope.py
@@ -218,13 +218,6 @@ class ScopeTests(unittest.TestCase):
""")
check_syntax_error(self, """if 1:
- def f(x):
- def g():
- return x
- del x # can't del name
- """)
-
- check_syntax_error(self, """if 1:
def f():
def g():
from sys import *
@@ -272,6 +265,28 @@ class ScopeTests(unittest.TestCase):
self.assertRaises(UnboundLocalError, errorInOuter)
self.assertRaises(NameError, errorInInner)
+ def testUnboundLocal_AfterDel(self):
+ # #4617: It is now legal to delete a cell variable.
+ # The following functions must obviously compile,
+ # and give the correct error when accessing the deleted name.
+ def errorInOuter():
+ y = 1
+ del y
+ print(y)
+ def inner():
+ return y
+
+ def errorInInner():
+ def inner():
+ return y
+ y = 1
+ del y
+ inner()
+
+ self.assertRaises(UnboundLocalError, errorInOuter)
+ self.assertRaises(NameError, errorInInner)
+
+ def testUnboundLocal_AugAssign(self):
# test for bug #1501934: incorrect LOAD/STORE_GLOBAL generation
exec("""if 1:
global_x = 1
diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py
index f354311..cd6b9a5 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -564,15 +564,6 @@ class SyntaxTestCase(unittest.TestCase):
def test_break_outside_loop(self):
self._check_error("break", "outside loop")
- def test_delete_deref(self):
- source = """if 1:
- def foo(x):
- def bar():
- print(x)
- del x
- """
- self._check_error(source, "nested scope")
-
def test_unexpected_indent(self):
self._check_error("foo()\n bar()\n", "unexpected indent",
subclass=IndentationError)