PEP 343 -- the with-statement.

This was started by Mike Bland and completed by Guido
(with help from Neal).

This still needs a __future__ statement added;
Thomas is working on Michael's patch for that aspect.

There's a small amount of code cleanup and refactoring
in ast.c, compile.c and ceval.c (I fixed the lltrace
behavior when EXT_POP is used -- however I had to make
lltrace a static global).
This commit is contained in:
Guido van Rossum 2006-02-27 22:32:47 +00:00
parent 5fec904f84
commit c2e20744b2
23 changed files with 1853 additions and 816 deletions

View File

@ -272,6 +272,11 @@
\lineiii{}{\member{else_}}{}
\hline
\lineiii{With}{\member{expr}}{}
\lineiii{}{\member{vars&}}{}
\lineiii{}{\member{body}}{}
\hline
\lineiii{Yield}{\member{value}}{}
\hline

View File

@ -308,6 +308,12 @@ section~\ref{exceptions}, and information on using the \keyword{raise}
statement to generate exceptions may be found in section~\ref{raise}.
\section{The \keyword{with} statement\label{with}}
\stindex{with}
The \keyword{with} statement specifies
\section{Function definitions\label{function}}
\indexii{function}{definition}
\stindex{def}

View File

@ -70,7 +70,7 @@ global_stmt: 'global' NAME (',' NAME)*
exec_stmt: 'exec' expr ['in' test [',' test]]
assert_stmt: 'assert' test [',' test]
compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef
compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
while_stmt: 'while' test ':' suite ['else' ':' suite]
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
@ -79,6 +79,8 @@ try_stmt: ('try' ':' suite
['else' ':' suite]
['finally' ':' suite] |
'finally' ':' suite))
with_stmt: 'with' test [ with_var ] ':' suite
with_var: NAME expr
# NB compile.c makes sure that the default except clause is last
except_clause: 'except' [test [',' test]]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT

View File

@ -61,11 +61,11 @@ struct _mod {
struct _stmt {
enum { FunctionDef_kind=1, ClassDef_kind=2, Return_kind=3,
Delete_kind=4, Assign_kind=5, AugAssign_kind=6, Print_kind=7,
For_kind=8, While_kind=9, If_kind=10, Raise_kind=11,
TryExcept_kind=12, TryFinally_kind=13, Assert_kind=14,
Import_kind=15, ImportFrom_kind=16, Exec_kind=17,
Global_kind=18, Expr_kind=19, Pass_kind=20, Break_kind=21,
Continue_kind=22 } kind;
For_kind=8, While_kind=9, If_kind=10, With_kind=11,
Raise_kind=12, TryExcept_kind=13, TryFinally_kind=14,
Assert_kind=15, Import_kind=16, ImportFrom_kind=17,
Exec_kind=18, Global_kind=19, Expr_kind=20, Pass_kind=21,
Break_kind=22, Continue_kind=23 } kind;
union {
struct {
identifier name;
@ -124,6 +124,12 @@ struct _stmt {
asdl_seq *orelse;
} If;
struct {
expr_ty context_expr;
expr_ty optional_vars;
asdl_seq *body;
} With;
struct {
expr_ty type;
expr_ty inst;
@ -355,6 +361,8 @@ stmt_ty While(expr_ty test, asdl_seq * body, asdl_seq * orelse, int lineno,
PyArena *arena);
stmt_ty If(expr_ty test, asdl_seq * body, asdl_seq * orelse, int lineno,
PyArena *arena);
stmt_ty With(expr_ty context_expr, expr_ty optional_vars, asdl_seq * body, int
lineno, PyArena *arena);
stmt_ty Raise(expr_ty type, expr_ty inst, expr_ty tback, int lineno, PyArena
*arena);
stmt_ty TryExcept(asdl_seq * body, asdl_seq * handlers, asdl_seq * orelse, int

View File

@ -38,45 +38,47 @@
#define while_stmt 293
#define for_stmt 294
#define try_stmt 295
#define except_clause 296
#define suite 297
#define testlist_safe 298
#define old_test 299
#define old_lambdef 300
#define test 301
#define or_test 302
#define and_test 303
#define not_test 304
#define comparison 305
#define comp_op 306
#define expr 307
#define xor_expr 308
#define and_expr 309
#define shift_expr 310
#define arith_expr 311
#define term 312
#define factor 313
#define power 314
#define atom 315
#define listmaker 316
#define testlist_gexp 317
#define lambdef 318
#define trailer 319
#define subscriptlist 320
#define subscript 321
#define sliceop 322
#define exprlist 323
#define testlist 324
#define dictmaker 325
#define classdef 326
#define arglist 327
#define argument 328
#define list_iter 329
#define list_for 330
#define list_if 331
#define gen_iter 332
#define gen_for 333
#define gen_if 334
#define testlist1 335
#define encoding_decl 336
#define yield_expr 337
#define with_stmt 296
#define with_var 297
#define except_clause 298
#define suite 299
#define testlist_safe 300
#define old_test 301
#define old_lambdef 302
#define test 303
#define or_test 304
#define and_test 305
#define not_test 306
#define comparison 307
#define comp_op 308
#define expr 309
#define xor_expr 310
#define and_expr 311
#define shift_expr 312
#define arith_expr 313
#define term 314
#define factor 315
#define power 316
#define atom 317
#define listmaker 318
#define testlist_gexp 319
#define lambdef 320
#define trailer 321
#define subscriptlist 322
#define subscript 323
#define sliceop 324
#define exprlist 325
#define testlist 326
#define dictmaker 327
#define classdef 328
#define arglist 329
#define argument 330
#define list_iter 331
#define list_for 332
#define list_if 333
#define gen_iter 334
#define gen_for 335
#define gen_if 336
#define testlist1 337
#define encoding_decl 338
#define yield_expr 339

View File

@ -72,13 +72,12 @@ extern "C" {
#define INPLACE_XOR 78
#define INPLACE_OR 79
#define BREAK_LOOP 80
#define WITH_CLEANUP 81
#define LOAD_LOCALS 82
#define RETURN_VALUE 83
#define IMPORT_STAR 84
#define EXEC_STMT 85
#define YIELD_VALUE 86
#define POP_BLOCK 87
#define END_FINALLY 88
#define BUILD_CLASS 89

View File

@ -553,7 +553,7 @@ class Function(Node):
self.varargs = 1
if flags & CO_VARKEYWORDS:
self.kwargs = 1
def getChildren(self):
@ -584,7 +584,7 @@ class GenExpr(Node):
self.lineno = lineno
self.argnames = ['[outmost-iterable]']
self.varargs = self.kwargs = None
def getChildren(self):
@ -763,7 +763,7 @@ class Lambda(Node):
self.varargs = 1
if flags & CO_VARKEYWORDS:
self.kwargs = 1
def getChildren(self):
@ -1297,6 +1297,31 @@ class While(Node):
def __repr__(self):
return "While(%s, %s, %s)" % (repr(self.test), repr(self.body), repr(self.else_))
class With(Node):
def __init__(self, expr, vars, body, lineno=None):
self.expr = expr
self.vars = vars
self.body = body
self.lineno = lineno
def getChildren(self):
children = []
children.append(self.expr)
children.append(self.vars)
children.append(self.body)
return tuple(children)
def getChildNodes(self):
nodelist = []
nodelist.append(self.expr)
if self.vars is not None:
nodelist.append(self.vars)
nodelist.append(self.body)
return tuple(nodelist)
def __repr__(self):
return "With(%s, %s, %s)" % (repr(self.expr), repr(self.vars), repr(self.body))
class Yield(Node):
def __init__(self, value, lineno=None):
self.value = value

View File

@ -41,6 +41,7 @@ def jabs_op(name, op):
hasjabs.append(op)
# Instruction opcodes for compiled code
# Blank lines correspond to available opcodes
def_op('STOP_CODE', 0)
def_op('POP_TOP', 1)
@ -59,7 +60,6 @@ def_op('UNARY_INVERT', 15)
def_op('LIST_APPEND', 18)
def_op('BINARY_POWER', 19)
def_op('BINARY_MULTIPLY', 20)
def_op('BINARY_DIVIDE', 21)
def_op('BINARY_MODULO', 22)
@ -70,7 +70,6 @@ def_op('BINARY_FLOOR_DIVIDE', 26)
def_op('BINARY_TRUE_DIVIDE', 27)
def_op('INPLACE_FLOOR_DIVIDE', 28)
def_op('INPLACE_TRUE_DIVIDE', 29)
def_op('SLICE+0', 30)
def_op('SLICE+1', 31)
def_op('SLICE+2', 32)
@ -93,7 +92,6 @@ def_op('INPLACE_DIVIDE', 58)
def_op('INPLACE_MODULO', 59)
def_op('STORE_SUBSCR', 60)
def_op('DELETE_SUBSCR', 61)
def_op('BINARY_LSHIFT', 62)
def_op('BINARY_RSHIFT', 63)
def_op('BINARY_AND', 64)
@ -113,13 +111,12 @@ def_op('INPLACE_AND', 77)
def_op('INPLACE_XOR', 78)
def_op('INPLACE_OR', 79)
def_op('BREAK_LOOP', 80)
def_op('WITH_CLEANUP', 81)
def_op('LOAD_LOCALS', 82)
def_op('RETURN_VALUE', 83)
def_op('IMPORT_STAR', 84)
def_op('EXEC_STMT', 85)
def_op('YIELD_VALUE', 86)
def_op('POP_BLOCK', 87)
def_op('END_FINALLY', 88)
def_op('BUILD_CLASS', 89)
@ -171,7 +168,6 @@ def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8)
def_op('MAKE_FUNCTION', 132) # Number of args with default values
def_op('BUILD_SLICE', 133) # Number of items
def_op('MAKE_CLOSURE', 134)
def_op('LOAD_CLOSURE', 135)
hasfree.append(135)
@ -183,7 +179,6 @@ hasfree.append(137)
def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8)
def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8)
def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8)
def_op('EXTENDED_ARG', 143)
EXTENDED_ARG = 143

View File

@ -50,48 +50,50 @@ if_stmt = 292
while_stmt = 293
for_stmt = 294
try_stmt = 295
except_clause = 296
suite = 297
testlist_safe = 298
old_test = 299
old_lambdef = 300
test = 301
or_test = 302
and_test = 303
not_test = 304
comparison = 305
comp_op = 306
expr = 307
xor_expr = 308
and_expr = 309
shift_expr = 310
arith_expr = 311
term = 312
factor = 313
power = 314
atom = 315
listmaker = 316
testlist_gexp = 317
lambdef = 318
trailer = 319
subscriptlist = 320
subscript = 321
sliceop = 322
exprlist = 323
testlist = 324
dictmaker = 325
classdef = 326
arglist = 327
argument = 328
list_iter = 329
list_for = 330
list_if = 331
gen_iter = 332
gen_for = 333
gen_if = 334
testlist1 = 335
encoding_decl = 336
yield_expr = 337
with_stmt = 296
with_var = 297
except_clause = 298
suite = 299
testlist_safe = 300
old_test = 301
old_lambdef = 302
test = 303
or_test = 304
and_test = 305
not_test = 306
comparison = 307
comp_op = 308
expr = 309
xor_expr = 310
and_expr = 311
shift_expr = 312
arith_expr = 313
term = 314
factor = 315
power = 316
atom = 317
listmaker = 318
testlist_gexp = 319
lambdef = 320
trailer = 321
subscriptlist = 322
subscript = 323
sliceop = 324
exprlist = 325
testlist = 326
dictmaker = 327
classdef = 328
arglist = 329
argument = 330
list_iter = 331
list_for = 332
list_if = 333
gen_iter = 334
gen_for = 335
gen_if = 336
testlist1 = 337
encoding_decl = 338
yield_expr = 339
#--end constants--
sym_name = {}

View File

@ -0,0 +1,34 @@
class GeneratorContextManager(object):
def __init__(self, gen):
self.gen = gen
def __context__(self):
return self
def __enter__(self):
try:
return self.gen.next()
except StopIteration:
raise RuntimeError("generator didn't yield")
def __exit__(self, type, value, traceback):
if type is None:
try:
self.gen.next()
except StopIteration:
return
else:
raise RuntimeError("generator didn't stop")
else:
try:
self.gen.throw(type, value, traceback)
except (type, StopIteration):
return
else:
raise RuntimeError("generator caught exception")
def contextmanager(func):
def helper(*args, **kwds):
return GeneratorContextManager(func(*args, **kwds))
return helper

41
Lib/test/nested.py Normal file
View File

@ -0,0 +1,41 @@
import sys
from collections import deque
class nested(object):
def __init__(self, *contexts):
self.contexts = contexts
self.entered = None
def __context__(self):
return self
def __enter__(self):
if self.entered is not None:
raise RuntimeError("Context is not reentrant")
self.entered = deque()
vars = []
try:
for context in self.contexts:
mgr = context.__context__()
vars.append(mgr.__enter__())
self.entered.appendleft(mgr)
except:
self.__exit__(*sys.exc_info())
raise
return vars
def __exit__(self, *exc_info):
# Behave like nested with statements
# first in, last out
# New exceptions override old ones
ex = exc_info
for mgr in self.entered:
try:
mgr.__exit__(*ex)
except:
ex = sys.exc_info()
self.entered = None
if ex is not exc_info:
raise ex[0], ex[1], ex[2]

560
Lib/test/test_with.py Normal file
View File

@ -0,0 +1,560 @@
#!/usr/bin/env python
"""Unit tests for the with statement specified in PEP 343."""
__author__ = "Mike Bland"
__email__ = "mbland at acm dot org"
import unittest
from test.contextmanager import GeneratorContextManager
from test.nested import nested
from test.test_support import run_unittest
class MockContextManager(GeneratorContextManager):
def __init__(self, gen):
GeneratorContextManager.__init__(self, gen)
self.context_called = False
self.enter_called = False
self.exit_called = False
self.exit_args = None
def __context__(self):
self.context_called = True
return GeneratorContextManager.__context__(self)
def __enter__(self):
self.enter_called = True
return GeneratorContextManager.__enter__(self)
def __exit__(self, type, value, traceback):
self.exit_called = True
self.exit_args = (type, value, traceback)
return GeneratorContextManager.__exit__(self, type, value, traceback)
def mock_contextmanager(func):
def helper(*args, **kwds):
return MockContextManager(func(*args, **kwds))
return helper
class MockResource(object):
def __init__(self):
self.yielded = False
self.stopped = False
@mock_contextmanager
def mock_contextmanager_generator():
mock = MockResource()
try:
mock.yielded = True
yield mock
finally:
mock.stopped = True
class MockNested(nested):
def __init__(self, *contexts):
nested.__init__(self, *contexts)
self.context_called = False
self.enter_called = False
self.exit_called = False
self.exit_args = None
def __context__(self):
self.context_called = True
return nested.__context__(self)
def __enter__(self):
self.enter_called = True
return nested.__enter__(self)
def __exit__(self, *exc_info):
self.exit_called = True
self.exit_args = exc_info
return nested.__exit__(self, *exc_info)
class FailureTestCase(unittest.TestCase):
def testNameError(self):
def fooNotDeclared():
with foo: pass
self.assertRaises(NameError, fooNotDeclared)
def testContextAttributeError(self):
class LacksContext(object):
def __enter__(self):
pass
def __exit__(self, type, value, traceback):
pass
def fooLacksContext():
foo = LacksContext()
with foo: pass
self.assertRaises(AttributeError, fooLacksContext)
def testEnterAttributeError(self):
class LacksEnter(object):
def __context__(self):
pass
def __exit__(self, type, value, traceback):
pass
def fooLacksEnter():
foo = LacksEnter()
with foo: pass
self.assertRaises(AttributeError, fooLacksEnter)
def testExitAttributeError(self):
class LacksExit(object):
def __context__(self):
pass
def __enter__(self):
pass
def fooLacksExit():
foo = LacksExit()
with foo: pass
self.assertRaises(AttributeError, fooLacksExit)
def assertRaisesSyntaxError(self, codestr):
def shouldRaiseSyntaxError(s):
compile(s, '', 'single')
self.assertRaises(SyntaxError, shouldRaiseSyntaxError, codestr)
def testAssignmentToNoneError(self):
self.assertRaisesSyntaxError('with mock as None:\n pass')
self.assertRaisesSyntaxError(
'with mock as (None):\n'
' pass')
def testAssignmentToEmptyTupleError(self):
self.assertRaisesSyntaxError(
'with mock as ():\n'
' pass')
def testAssignmentToTupleOnlyContainingNoneError(self):
self.assertRaisesSyntaxError('with mock as None,:\n pass')
self.assertRaisesSyntaxError(
'with mock as (None,):\n'
' pass')
def testAssignmentToTupleContainingNoneError(self):
self.assertRaisesSyntaxError(
'with mock as (foo, None, bar):\n'
' pass')
def testContextThrows(self):
class ContextThrows(object):
def __context__(self):
raise RuntimeError("Context threw")
def shouldThrow():
ct = ContextThrows()
self.foo = None
with ct as self.foo:
pass
self.assertRaises(RuntimeError, shouldThrow)
self.assertEqual(self.foo, None)
def testEnterThrows(self):
class EnterThrows(object):
def __context__(self):
return self
def __enter__(self):
raise RuntimeError("Context threw")
def __exit__(self, *args):
pass
def shouldThrow():
ct = EnterThrows()
self.foo = None
with ct as self.foo:
pass
self.assertRaises(RuntimeError, shouldThrow)
self.assertEqual(self.foo, None)
def testExitThrows(self):
class ExitThrows(object):
def __context__(self):
return self
def __enter__(self):
return
def __exit__(self, *args):
raise RuntimeError(42)
def shouldThrow():
with ExitThrows():
pass
self.assertRaises(RuntimeError, shouldThrow)
class ContextmanagerAssertionMixin(object):
TEST_EXCEPTION = RuntimeError("test exception")
def assertInWithManagerInvariants(self, mock_manager):
self.assertTrue(mock_manager.context_called)
self.assertTrue(mock_manager.enter_called)
self.assertFalse(mock_manager.exit_called)
self.assertEqual(mock_manager.exit_args, None)
def assertAfterWithManagerInvariants(self, mock_manager, exit_args):
self.assertTrue(mock_manager.context_called)
self.assertTrue(mock_manager.enter_called)
self.assertTrue(mock_manager.exit_called)
self.assertEqual(mock_manager.exit_args, exit_args)
def assertAfterWithManagerInvariantsNoError(self, mock_manager):
self.assertAfterWithManagerInvariants(mock_manager,
(None, None, None))
def assertInWithGeneratorInvariants(self, mock_generator):
self.assertTrue(mock_generator.yielded)
self.assertFalse(mock_generator.stopped)
def assertAfterWithGeneratorInvariantsNoError(self, mock_generator):
self.assertTrue(mock_generator.yielded)
self.assertTrue(mock_generator.stopped)
def raiseTestException(self):
raise self.TEST_EXCEPTION
def assertAfterWithManagerInvariantsWithError(self, mock_manager):
self.assertTrue(mock_manager.context_called)
self.assertTrue(mock_manager.enter_called)
self.assertTrue(mock_manager.exit_called)
self.assertEqual(mock_manager.exit_args[0], RuntimeError)
self.assertEqual(mock_manager.exit_args[1], self.TEST_EXCEPTION)
def assertAfterWithGeneratorInvariantsWithError(self, mock_generator):
self.assertTrue(mock_generator.yielded)
self.assertTrue(mock_generator.stopped)
class NonexceptionalTestCase(unittest.TestCase, ContextmanagerAssertionMixin):
def testInlineGeneratorSyntax(self):
with mock_contextmanager_generator():
pass
def testUnboundGenerator(self):
mock = mock_contextmanager_generator()
with mock:
pass
self.assertAfterWithManagerInvariantsNoError(mock)
def testInlineGeneratorBoundSyntax(self):
with mock_contextmanager_generator() as foo:
self.assertInWithGeneratorInvariants(foo)
# FIXME: In the future, we'll try to keep the bound names from leaking
self.assertAfterWithGeneratorInvariantsNoError(foo)
def testInlineGeneratorBoundToExistingVariable(self):
foo = None
with mock_contextmanager_generator() as foo:
self.assertInWithGeneratorInvariants(foo)
self.assertAfterWithGeneratorInvariantsNoError(foo)
def testInlineGeneratorBoundToDottedVariable(self):
with mock_contextmanager_generator() as self.foo:
self.assertInWithGeneratorInvariants(self.foo)
self.assertAfterWithGeneratorInvariantsNoError(self.foo)
def testBoundGenerator(self):
mock = mock_contextmanager_generator()
with mock as foo:
self.assertInWithGeneratorInvariants(foo)
self.assertInWithManagerInvariants(mock)
self.assertAfterWithGeneratorInvariantsNoError(foo)
self.assertAfterWithManagerInvariantsNoError(mock)
def testNestedSingleStatements(self):
mock_a = mock_contextmanager_generator()
with mock_a as foo:
mock_b = mock_contextmanager_generator()
with mock_b as bar:
self.assertInWithManagerInvariants(mock_a)
self.assertInWithManagerInvariants(mock_b)
self.assertInWithGeneratorInvariants(foo)
self.assertInWithGeneratorInvariants(bar)
self.assertAfterWithManagerInvariantsNoError(mock_b)
self.assertAfterWithGeneratorInvariantsNoError(bar)
self.assertInWithManagerInvariants(mock_a)
self.assertInWithGeneratorInvariants(foo)
self.assertAfterWithManagerInvariantsNoError(mock_a)
self.assertAfterWithGeneratorInvariantsNoError(foo)
class NestedNonexceptionalTestCase(unittest.TestCase,
ContextmanagerAssertionMixin):
def testSingleArgInlineGeneratorSyntax(self):
with nested(mock_contextmanager_generator()):
pass
def testSingleArgUnbound(self):
mock_contextmanager = mock_contextmanager_generator()
mock_nested = MockNested(mock_contextmanager)
with mock_nested:
self.assertInWithManagerInvariants(mock_contextmanager)
self.assertInWithManagerInvariants(mock_nested)
self.assertAfterWithManagerInvariantsNoError(mock_contextmanager)
self.assertAfterWithManagerInvariantsNoError(mock_nested)
def testSingleArgBoundToNonTuple(self):
m = mock_contextmanager_generator()
# This will bind all the arguments to nested() into a single list
# assigned to foo.
with nested(m) as foo:
self.assertInWithManagerInvariants(m)
self.assertAfterWithManagerInvariantsNoError(m)
def testSingleArgBoundToSingleElementParenthesizedList(self):
m = mock_contextmanager_generator()
# This will bind all the arguments to nested() into a single list
# assigned to foo.
# FIXME: what should this do: with nested(m) as (foo,):
with nested(m) as (foo):
self.assertInWithManagerInvariants(m)
self.assertAfterWithManagerInvariantsNoError(m)
def testSingleArgBoundToMultipleElementTupleError(self):
def shouldThrowValueError():
with nested(mock_contextmanager_generator()) as (foo, bar):
pass
self.assertRaises(ValueError, shouldThrowValueError)
def testSingleArgUnbound(self):
mock_contextmanager = mock_contextmanager_generator()
mock_nested = MockNested(mock_contextmanager)
with mock_nested:
self.assertInWithManagerInvariants(mock_contextmanager)
self.assertInWithManagerInvariants(mock_nested)
self.assertAfterWithManagerInvariantsNoError(mock_contextmanager)
self.assertAfterWithManagerInvariantsNoError(mock_nested)
def testMultipleArgUnbound(self):
m = mock_contextmanager_generator()
n = mock_contextmanager_generator()
o = mock_contextmanager_generator()
mock_nested = MockNested(m, n, o)
with mock_nested:
self.assertInWithManagerInvariants(m)
self.assertInWithManagerInvariants(n)
self.assertInWithManagerInvariants(o)
self.assertInWithManagerInvariants(mock_nested)
self.assertAfterWithManagerInvariantsNoError(m)
self.assertAfterWithManagerInvariantsNoError(n)
self.assertAfterWithManagerInvariantsNoError(o)
self.assertAfterWithManagerInvariantsNoError(mock_nested)
def testMultipleArgBound(self):
mock_nested = MockNested(mock_contextmanager_generator(),
mock_contextmanager_generator(), mock_contextmanager_generator())
with mock_nested as (m, n, o):
self.assertInWithGeneratorInvariants(m)
self.assertInWithGeneratorInvariants(n)
self.assertInWithGeneratorInvariants(o)
self.assertInWithManagerInvariants(mock_nested)
self.assertAfterWithGeneratorInvariantsNoError(m)
self.assertAfterWithGeneratorInvariantsNoError(n)
self.assertAfterWithGeneratorInvariantsNoError(o)
self.assertAfterWithManagerInvariantsNoError(mock_nested)
class ExceptionalTestCase(unittest.TestCase, ContextmanagerAssertionMixin):
def testSingleResource(self):
cm = mock_contextmanager_generator()
def shouldThrow():
with cm as self.resource:
self.assertInWithManagerInvariants(cm)
self.assertInWithGeneratorInvariants(self.resource)
self.raiseTestException()
self.assertRaises(RuntimeError, shouldThrow)
self.assertAfterWithManagerInvariantsWithError(cm)
self.assertAfterWithGeneratorInvariantsWithError(self.resource)
def testNestedSingleStatements(self):
mock_a = mock_contextmanager_generator()
mock_b = mock_contextmanager_generator()
def shouldThrow():
with mock_a as self.foo:
with mock_b as self.bar:
self.assertInWithManagerInvariants(mock_a)
self.assertInWithManagerInvariants(mock_b)
self.assertInWithGeneratorInvariants(self.foo)
self.assertInWithGeneratorInvariants(self.bar)
self.raiseTestException()
self.assertRaises(RuntimeError, shouldThrow)
self.assertAfterWithManagerInvariantsWithError(mock_a)
self.assertAfterWithManagerInvariantsWithError(mock_b)
self.assertAfterWithGeneratorInvariantsWithError(self.foo)
self.assertAfterWithGeneratorInvariantsWithError(self.bar)
def testMultipleResourcesInSingleStatement(self):
cm_a = mock_contextmanager_generator()
cm_b = mock_contextmanager_generator()
mock_nested = MockNested(cm_a, cm_b)
def shouldThrow():
with mock_nested as (self.resource_a, self.resource_b):
self.assertInWithManagerInvariants(cm_a)
self.assertInWithManagerInvariants(cm_b)
self.assertInWithManagerInvariants(mock_nested)
self.assertInWithGeneratorInvariants(self.resource_a)
self.assertInWithGeneratorInvariants(self.resource_b)
self.raiseTestException()
self.assertRaises(RuntimeError, shouldThrow)
self.assertAfterWithManagerInvariantsWithError(cm_a)
self.assertAfterWithManagerInvariantsWithError(cm_b)
self.assertAfterWithManagerInvariantsWithError(mock_nested)
self.assertAfterWithGeneratorInvariantsWithError(self.resource_a)
self.assertAfterWithGeneratorInvariantsWithError(self.resource_b)
def testNestedExceptionBeforeInnerStatement(self):
mock_a = mock_contextmanager_generator()
mock_b = mock_contextmanager_generator()
self.bar = None
def shouldThrow():
with mock_a as self.foo:
self.assertInWithManagerInvariants(mock_a)
self.assertInWithGeneratorInvariants(self.foo)
self.raiseTestException()
with mock_b as self.bar:
pass
self.assertRaises(RuntimeError, shouldThrow)
self.assertAfterWithManagerInvariantsWithError(mock_a)
self.assertAfterWithGeneratorInvariantsWithError(self.foo)
# The inner statement stuff should never have been touched
self.assertEqual(self.bar, None)
self.assertFalse(mock_b.context_called)
self.assertFalse(mock_b.enter_called)
self.assertFalse(mock_b.exit_called)
self.assertEqual(mock_b.exit_args, None)
def testNestedExceptionAfterInnerStatement(self):
mock_a = mock_contextmanager_generator()
mock_b = mock_contextmanager_generator()
def shouldThrow():
with mock_a as self.foo:
with mock_b as self.bar:
self.assertInWithManagerInvariants(mock_a)
self.assertInWithManagerInvariants(mock_b)
self.assertInWithGeneratorInvariants(self.foo)
self.assertInWithGeneratorInvariants(self.bar)
self.raiseTestException()
self.assertRaises(RuntimeError, shouldThrow)
self.assertAfterWithManagerInvariantsWithError(mock_a)
self.assertAfterWithManagerInvariantsNoError(mock_b)
self.assertAfterWithGeneratorInvariantsWithError(self.foo)
self.assertAfterWithGeneratorInvariantsNoError(self.bar)
class NonLocalFlowControlTestCase(unittest.TestCase):
def testWithBreak(self):
counter = 0
while True:
counter += 1
with mock_contextmanager_generator():
counter += 10
break
counter += 100 # Not reached
self.assertEqual(counter, 11)
def testWithContinue(self):
counter = 0
while True:
counter += 1
if counter > 2:
break
with mock_contextmanager_generator():
counter += 10
continue
counter += 100 # Not reached
self.assertEqual(counter, 12)
def testWithReturn(self):
def foo():
counter = 0
while True:
counter += 1
with mock_contextmanager_generator():
counter += 10
return counter
counter += 100 # Not reached
self.assertEqual(foo(), 11)
def testWithYield(self):
def gen():
with mock_contextmanager_generator():
yield 12
yield 13
x = list(gen())
self.assertEqual(x, [12, 13])
def testWithRaise(self):
counter = 0
try:
counter += 1
with mock_contextmanager_generator():
counter += 10
raise RuntimeError
counter += 100 # Not reached
except RuntimeError:
self.assertEqual(counter, 11)
else:
self.fail("Didn't raise RuntimeError")
class AssignmentTargetTestCase(unittest.TestCase):
def testSingleComplexTarget(self):
targets = {1: [0, 1, 2]}
with mock_contextmanager_generator() as targets[1][0]:
self.assertEqual(targets.keys(), [1])
self.assertEqual(targets[1][0].__class__, MockResource)
with mock_contextmanager_generator() as targets.values()[0][1]:
self.assertEqual(targets.keys(), [1])
self.assertEqual(targets[1][1].__class__, MockResource)
with mock_contextmanager_generator() as targets[2]:
keys = targets.keys()
keys.sort()
self.assertEqual(keys, [1, 2])
class C: pass
blah = C()
with mock_contextmanager_generator() as blah.foo:
self.assertEqual(hasattr(blah, "foo"), True)
def testMultipleComplexTargets(self):
class C:
def __context__(self): return self
def __enter__(self): return 1, 2, 3
def __exit__(self, *a): pass
targets = {1: [0, 1, 2]}
with C() as (targets[1][0], targets[1][1], targets[1][2]):
self.assertEqual(targets, {1: [1, 2, 3]})
with C() as (targets.values()[0][2], targets.values()[0][1], targets.values()[0][0]):
self.assertEqual(targets, {1: [3, 2, 1]})
with C() as (targets[1], targets[2], targets[3]):
self.assertEqual(targets, {1: 1, 2: 2, 3: 3})
class B: pass
blah = B()
with C() as (blah.one, blah.two, blah.three):
self.assertEqual(blah.one, 1)
self.assertEqual(blah.two, 2)
self.assertEqual(blah.three, 3)
def test_main():
run_unittest(FailureTestCase, NonexceptionalTestCase,
NestedNonexceptionalTestCase, ExceptionalTestCase,
NonLocalFlowControlTestCase,
AssignmentTargetTestCase)
if __name__ == '__main__':
test_main()

View File

@ -62,6 +62,7 @@ Dominic Binks
Philippe Biondi
Stuart Bishop
Roy Bixler
Mike Bland
Martin Bless
Pablo Bleyer
Erik van Blokland

View File

@ -19,6 +19,8 @@ Core and builtins
- dict.__getitem__ now looks for a __missing__ hook before raising
KeyError.
- PEP 343: with statement implemented.
- Fix the encodings package codec search function to only search
inside its own package. Fixes problem reported in patch #1433198.

View File

@ -25,6 +25,7 @@ module Python
| For(expr target, expr iter, stmt* body, stmt* orelse)
| While(expr test, stmt* body, stmt* orelse)
| If(expr test, stmt* body, stmt* orelse)
| With(expr context_expr, expr? optional_vars, stmt* body)
-- 'type' is a bad name
| Raise(expr? type, expr? inst, expr? tback)

View File

@ -84,6 +84,12 @@ char *If_fields[]={
"body",
"orelse",
};
PyTypeObject *With_type;
char *With_fields[]={
"context_expr",
"optional_vars",
"body",
};
PyTypeObject *Raise_type;
char *Raise_fields[]={
"type",
@ -465,6 +471,8 @@ static int init_types(void)
if (!While_type) return 0;
If_type = make_type("If", stmt_type, If_fields, 3);
if (!If_type) return 0;
With_type = make_type("With", stmt_type, With_fields, 3);
if (!With_type) return 0;
Raise_type = make_type("Raise", stmt_type, Raise_fields, 3);
if (!Raise_type) return 0;
TryExcept_type = make_type("TryExcept", stmt_type, TryExcept_fields, 3);
@ -999,6 +1007,29 @@ If(expr_ty test, asdl_seq * body, asdl_seq * orelse, int lineno, PyArena *arena)
return p;
}
stmt_ty
With(expr_ty context_expr, expr_ty optional_vars, asdl_seq * body, int lineno,
PyArena *arena)
{
stmt_ty p;
if (!context_expr) {
PyErr_SetString(PyExc_ValueError,
"field context_expr is required for With");
return NULL;
}
p = (stmt_ty)PyArena_Malloc(arena, sizeof(*p));
if (!p) {
PyErr_NoMemory();
return NULL;
}
p->kind = With_kind;
p->v.With.context_expr = context_expr;
p->v.With.optional_vars = optional_vars;
p->v.With.body = body;
p->lineno = lineno;
return p;
}
stmt_ty
Raise(expr_ty type, expr_ty inst, expr_ty tback, int lineno, PyArena *arena)
{
@ -2062,6 +2093,26 @@ ast2obj_stmt(void* _o)
goto failed;
Py_DECREF(value);
break;
case With_kind:
result = PyType_GenericNew(With_type, NULL, NULL);
if (!result) goto failed;
value = ast2obj_expr(o->v.With.context_expr);
if (!value) goto failed;
if (PyObject_SetAttrString(result, "context_expr", value) == -1)
goto failed;
Py_DECREF(value);
value = ast2obj_expr(o->v.With.optional_vars);
if (!value) goto failed;
if (PyObject_SetAttrString(result, "optional_vars", value) ==
-1)
goto failed;
Py_DECREF(value);
value = ast2obj_list(o->v.With.body, ast2obj_stmt);
if (!value) goto failed;
if (PyObject_SetAttrString(result, "body", value) == -1)
goto failed;
Py_DECREF(value);
break;
case Raise_kind:
result = PyType_GenericNew(Raise_type, NULL, NULL);
if (!result) goto failed;
@ -2922,6 +2973,7 @@ init_ast(void)
if(PyDict_SetItemString(d, "For", (PyObject*)For_type) < 0) return;
if(PyDict_SetItemString(d, "While", (PyObject*)While_type) < 0) return;
if(PyDict_SetItemString(d, "If", (PyObject*)If_type) < 0) return;
if(PyDict_SetItemString(d, "With", (PyObject*)With_type) < 0) return;
if(PyDict_SetItemString(d, "Raise", (PyObject*)Raise_type) < 0) return;
if(PyDict_SetItemString(d, "TryExcept", (PyObject*)TryExcept_type) < 0)
return;

View File

@ -314,7 +314,7 @@ get_operator(const node *n)
}
}
/* Set the context ctx for expr_ty e returning 0 on success, -1 on error.
/* Set the context ctx for expr_ty e returning 1 on success, 0 on error.
Only sets context for expr kinds that "can appear in assignment context"
(according to ../Parser/Python.asdl). For other expr kinds, it sets
@ -339,7 +339,7 @@ set_context(expr_ty e, expr_context_ty ctx, const node *n)
a little more complex than necessary as a result. It also means
that expressions in an augmented assignment have no context.
Consider restructuring so that augmented assignment uses
set_context(), too
set_context(), too.
*/
assert(ctx != AugStore && ctx != AugLoad);
@ -2713,6 +2713,46 @@ ast_for_try_stmt(struct compiling *c, const node *n)
return TryFinally(body, finally, LINENO(n), c->c_arena);
}
static expr_ty
ast_for_with_var(struct compiling *c, const node *n)
{
REQ(n, with_var);
if (strcmp(STR(CHILD(n, 0)), "as") != 0) {
ast_error(n, "expected \"with [expr] as [var]\"");
return NULL;
}
return ast_for_expr(c, CHILD(n, 1));
}
/* with_stmt: 'with' test [ with_var ] ':' suite */
static stmt_ty
ast_for_with_stmt(struct compiling *c, const node *n)
{
expr_ty context_expr, optional_vars = NULL;
int suite_index = 3; /* skip 'with', test, and ':' */
asdl_seq *suite_seq;
assert(TYPE(n) == with_stmt);
context_expr = ast_for_expr(c, CHILD(n, 1));
if (TYPE(CHILD(n, 2)) == with_var) {
optional_vars = ast_for_with_var(c, CHILD(n, 2));
if (!optional_vars) {
return NULL;
}
if (!set_context(optional_vars, Store, n)) {
return NULL;
}
suite_index = 4;
}
suite_seq = ast_for_suite(c, CHILD(n, suite_index));
if (!suite_seq) {
return NULL;
}
return With(context_expr, optional_vars, suite_seq, LINENO(n), c->c_arena);
}
static stmt_ty
ast_for_classdef(struct compiling *c, const node *n)
{
@ -2813,6 +2853,8 @@ ast_for_stmt(struct compiling *c, const node *n)
return ast_for_for_stmt(c, ch);
case try_stmt:
return ast_for_try_stmt(c, ch);
case with_stmt:
return ast_for_with_stmt(c, ch);
case funcdef:
return ast_for_funcdef(c, ch);
case classdef:

View File

@ -97,6 +97,7 @@ static PyObject *load_args(PyObject ***, int);
#define CALL_FLAG_KW 2
#ifdef LLTRACE
static int lltrace;
static int prtrace(PyObject *, char *);
#endif
static int call_trace(Py_tracefunc, PyObject *, PyFrameObject *,
@ -540,9 +541,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw)
unsigned char *first_instr;
PyObject *names;
PyObject *consts;
#ifdef LLTRACE
int lltrace;
#endif
#if defined(Py_DEBUG) || defined(LLTRACE)
/* Make it easier to find out where we are with a debugger */
char *filename;
@ -661,10 +659,12 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw)
#define STACKADJ(n) { (void)(BASIC_STACKADJ(n), \
lltrace && prtrace(TOP(), "stackadj")); \
assert(STACK_LEVEL() <= f->f_stacksize); }
#define EXT_POP(STACK_POINTER) (lltrace && prtrace(*(STACK_POINTER), "ext_pop"), *--(STACK_POINTER))
#else
#define PUSH(v) BASIC_PUSH(v)
#define POP() BASIC_POP()
#define STACKADJ(n) BASIC_STACKADJ(n)
#define EXT_POP(STACK_POINTER) (*--(STACK_POINTER))
#endif
/* Local variable macros */
@ -2172,6 +2172,43 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw)
STACK_LEVEL());
continue;
case WITH_CLEANUP:
{
/* TOP is the context.__exit__ bound method.
Below that are 1-3 values indicating how/why
we entered the finally clause:
- SECOND = None
- (SECOND, THIRD) = (WHY_RETURN or WHY_CONTINUE), retval
- SECOND = WHY_*; no retval below it
- (SECOND, THIRD, FOURTH) = exc_info()
In the last case, we must call
TOP(SECOND, THIRD, FOURTH)
otherwise we must call
TOP(None, None, None)
but we must preserve the stack entries below TOP.
The code here just sets the stack up for the call;
separate CALL_FUNCTION(3) and POP_TOP opcodes are
emitted by the compiler.
*/
x = TOP();
u = SECOND();
if (PyInt_Check(u) || u == Py_None) {
u = v = w = Py_None;
}
else {
v = THIRD();
w = FOURTH();
}
Py_INCREF(u);
Py_INCREF(v);
Py_INCREF(w);
PUSH(u);
PUSH(v);
PUSH(w);
break;
}
case CALL_FUNCTION:
{
PyObject **sp;
@ -2511,9 +2548,9 @@ fast_yield:
return retval;
}
/* this is gonna seem *real weird*, but if you put some other code between
/* This is gonna seem *real weird*, but if you put some other code between
PyEval_EvalFrame() and PyEval_EvalCodeEx() you will need to adjust
the test in the if statement in Misc/gdbinit:pystack* */
the test in the if statements in Misc/gdbinit (pystack and pystackv). */
PyObject *
PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
@ -3473,8 +3510,6 @@ PyEval_GetFuncDesc(PyObject *func)
}
}
#define EXT_POP(STACK_POINTER) (*--(STACK_POINTER))
static void
err_args(PyObject *func, int flags, int nargs)
{

View File

@ -191,6 +191,8 @@ static void compiler_pop_fblock(struct compiler *, enum fblocktype,
static int inplace_binop(struct compiler *, operator_ty);
static int expr_constant(expr_ty e);
static int compiler_with(struct compiler *, stmt_ty);
static PyCodeObject *assemble(struct compiler *, int addNone);
static PyObject *__doc__;
@ -289,6 +291,7 @@ PyAST_Compile(mod_ty mod, const char *filename, PyCompilerFlags *flags,
error:
compiler_free(&c);
assert(!PyErr_Occurred());
return co;
}
@ -1157,6 +1160,18 @@ compiler_exit_scope(struct compiler *c)
}
/* Allocate a new "anonymous" local variable.
Used by list comprehensions and with statements.
*/
static PyObject *
compiler_new_tmpname(struct compiler *c)
{
char tmpname[256];
PyOS_snprintf(tmpname, sizeof(tmpname), "_[%d]", ++c->u->u_tmpname);
return PyString_FromString(tmpname);
}
/* Allocate a new block and return a pointer to it.
Returns NULL on error.
*/
@ -1360,7 +1375,8 @@ opcode_stack_effect(int opcode, int oparg)
return -1;
case BREAK_LOOP:
return 0;
case WITH_CLEANUP:
return 3;
case LOAD_LOCALS:
return 1;
case RETURN_VALUE:
@ -2663,6 +2679,8 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
break;
case Continue_kind:
return compiler_continue(c);
case With_kind:
return compiler_with(c, s);
}
return 1;
}
@ -3124,7 +3142,6 @@ compiler_listcomp_generator(struct compiler *c, PyObject *tmpname,
static int
compiler_listcomp(struct compiler *c, expr_ty e)
{
char tmpname[256];
identifier tmp;
int rc = 0;
static identifier append;
@ -3136,8 +3153,7 @@ compiler_listcomp(struct compiler *c, expr_ty e)
if (!append)
return 0;
}
PyOS_snprintf(tmpname, sizeof(tmpname), "_[%d]", ++c->u->u_tmpname);
tmp = PyString_FromString(tmpname);
tmp = compiler_new_tmpname(c);
if (!tmp)
return 0;
ADDOP_I(c, BUILD_LIST, 0);
@ -3291,6 +3307,148 @@ expr_constant(expr_ty e)
}
}
/*
Implements the with statement from PEP 343.
The semantics outlined in that PEP are as follows:
with EXPR as VAR:
BLOCK
It is implemented roughly as:
context = (EXPR).__context__()
exit = context.__exit__ # not calling it
value = context.__enter__()
try:
VAR = value # if VAR present in the syntax
BLOCK
finally:
if an exception was raised:
exc = copy of (exception, instance, traceback)
else:
exc = (None, None, None)
exit(*exc)
*/
static int
compiler_with(struct compiler *c, stmt_ty s)
{
static identifier context_attr, enter_attr, exit_attr;
basicblock *block, *finally;
identifier tmpexit, tmpvalue = NULL;
assert(s->kind == With_kind);
if (!context_attr) {
context_attr = PyString_InternFromString("__context__");
if (!context_attr)
return 0;
}
if (!enter_attr) {
enter_attr = PyString_InternFromString("__enter__");
if (!enter_attr)
return 0;
}
if (!exit_attr) {
exit_attr = PyString_InternFromString("__exit__");
if (!exit_attr)
return 0;
}
block = compiler_new_block(c);
finally = compiler_new_block(c);
if (!block || !finally)
return 0;
/* Create a temporary variable to hold context.__exit__ */
tmpexit = compiler_new_tmpname(c);
if (tmpexit == NULL)
return 0;
PyArena_AddPyObject(c->c_arena, tmpexit);
if (s->v.With.optional_vars) {
/* Create a temporary variable to hold context.__enter__().
We need to do this rather than preserving it on the stack
because SETUP_FINALLY remembers the stack level.
We need to do the assignment *inside* the try/finally
so that context.__exit__() is called when the assignment
fails. But we need to call context.__enter__() *before*
the try/finally so that if it fails we won't call
context.__exit__().
*/
tmpvalue = compiler_new_tmpname(c);
if (tmpvalue == NULL)
return 0;
PyArena_AddPyObject(c->c_arena, tmpvalue);
}
/* Evaluate (EXPR).__context__() */
VISIT(c, expr, s->v.With.context_expr);
ADDOP_O(c, LOAD_ATTR, context_attr, names);
ADDOP_I(c, CALL_FUNCTION, 0);
/* Squirrel away context.__exit__ */
ADDOP(c, DUP_TOP);
ADDOP_O(c, LOAD_ATTR, exit_attr, names);
if (!compiler_nameop(c, tmpexit, Store))
return 0;
/* Call context.__enter__() */
ADDOP_O(c, LOAD_ATTR, enter_attr, names);
ADDOP_I(c, CALL_FUNCTION, 0);
if (s->v.With.optional_vars) {
/* Store it in tmpvalue */
if (!compiler_nameop(c, tmpvalue, Store))
return 0;
}
else {
/* Discard result from context.__enter__() */
ADDOP(c, POP_TOP);
}
/* Start the try block */
ADDOP_JREL(c, SETUP_FINALLY, finally);
compiler_use_next_block(c, block);
if (!compiler_push_fblock(c, FINALLY_TRY, block)) {
return 0;
}
if (s->v.With.optional_vars) {
/* Bind saved result of context.__enter__() to VAR */
if (!compiler_nameop(c, tmpvalue, Load) ||
!compiler_nameop(c, tmpvalue, Del))
return 0;
VISIT(c, expr, s->v.With.optional_vars);
}
/* BLOCK code */
VISIT_SEQ(c, stmt, s->v.With.body);
/* End of try block; start the finally block */
ADDOP(c, POP_BLOCK);
compiler_pop_fblock(c, FINALLY_TRY, block);
ADDOP_O(c, LOAD_CONST, Py_None, consts);
compiler_use_next_block(c, finally);
if (!compiler_push_fblock(c, FINALLY_END, finally))
return 0;
/* Finally block starts; push tmpexit and issue our magic opcode. */
if (!compiler_nameop(c, tmpexit, Load) ||
!compiler_nameop(c, tmpexit, Del))
return 0;
ADDOP(c, WITH_CLEANUP);
ADDOP_I(c, CALL_FUNCTION, 3);
ADDOP(c, POP_TOP);
/* Finally block ends. */
ADDOP(c, END_FINALLY);
compiler_pop_fblock(c, FINALLY_END, finally);
return 1;
}
static int
compiler_visit_expr(struct compiler *c, expr_ty e)
{

File diff suppressed because it is too large Load Diff

View File

@ -54,9 +54,10 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *);
Python 2.4b1: 62061
Python 2.5a0: 62071
Python 2.5a0: 62081 (ast-branch)
Python 2.5a0: 62091 (with)
.
*/
#define MAGIC (62081 | ((long)'\r'<<16) | ((long)'\n'<<24))
#define MAGIC (62091 | ((long)'\r'<<16) | ((long)'\n'<<24))
/* Magic word as global; note that _PyImport_Init() can change the
value of this global to accommodate for alterations of how the

View File

@ -890,6 +890,21 @@ error:
} \
}
static int
symtable_new_tmpname(struct symtable *st)
{
char tmpname[256];
identifier tmp;
PyOS_snprintf(tmpname, sizeof(tmpname), "_[%d]",
++st->st_cur->ste_tmpname);
tmp = PyString_InternFromString(tmpname);
if (!symtable_add_def(st, tmp, DEF_LOCAL))
return 0;
Py_DECREF(tmp);
return 1;
}
static int
symtable_visit_stmt(struct symtable *st, stmt_ty s)
{
@ -1051,6 +1066,17 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
case Continue_kind:
/* nothing to do here */
break;
case With_kind:
if (!symtable_new_tmpname(st))
return 0;
VISIT(st, expr, s->v.With.context_expr);
if (s->v.With.optional_vars) {
if (!symtable_new_tmpname(st))
return 0;
VISIT(st, expr, s->v.With.optional_vars);
}
VISIT_SEQ(st, stmt, s->v.With.body);
break;
}
return 1;
}
@ -1093,26 +1119,16 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
VISIT_SEQ(st, expr, e->v.Dict.keys);
VISIT_SEQ(st, expr, e->v.Dict.values);
break;
case ListComp_kind: {
char tmpname[256];
identifier tmp;
PyOS_snprintf(tmpname, sizeof(tmpname), "_[%d]",
++st->st_cur->ste_tmpname);
tmp = PyString_InternFromString(tmpname);
if (!symtable_add_def(st, tmp, DEF_LOCAL))
case ListComp_kind:
if (!symtable_new_tmpname(st))
return 0;
Py_DECREF(tmp);
VISIT(st, expr, e->v.ListComp.elt);
VISIT_SEQ(st, comprehension, e->v.ListComp.generators);
break;
}
case GeneratorExp_kind: {
if (!symtable_visit_genexp(st, e)) {
case GeneratorExp_kind:
if (!symtable_visit_genexp(st, e))
return 0;
}
break;
}
case Yield_kind:
if (e->v.Yield.value)
VISIT(st, expr, e->v.Yield.value);

View File

@ -20,6 +20,7 @@ Break:
Continue:
For: assign, list, body, else_&
While: test, body, else_&
With: expr, vars&, body
If: tests!, else_&
Exec: expr, locals&, globals&
From: modname*, names*
@ -42,7 +43,7 @@ AssAttr: expr, attrname*, flags*
ListComp: expr, quals!
ListCompFor: assign, list, ifs!
ListCompIf: test
GenExpr: code
GenExpr: code
GenExprInner: expr, quals!
GenExprFor: assign, iter, ifs!
GenExprIf: test