mirror of
https://github.com/python/cpython.git
synced 2025-08-03 00:23:06 +00:00
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:
parent
5fec904f84
commit
c2e20744b2
23 changed files with 1853 additions and 816 deletions
|
@ -272,6 +272,11 @@
|
||||||
\lineiii{}{\member{else_}}{}
|
\lineiii{}{\member{else_}}{}
|
||||||
\hline
|
\hline
|
||||||
|
|
||||||
|
\lineiii{With}{\member{expr}}{}
|
||||||
|
\lineiii{}{\member{vars&}}{}
|
||||||
|
\lineiii{}{\member{body}}{}
|
||||||
|
\hline
|
||||||
|
|
||||||
\lineiii{Yield}{\member{value}}{}
|
\lineiii{Yield}{\member{value}}{}
|
||||||
\hline
|
\hline
|
||||||
|
|
||||||
|
|
|
@ -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}.
|
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}}
|
\section{Function definitions\label{function}}
|
||||||
\indexii{function}{definition}
|
\indexii{function}{definition}
|
||||||
\stindex{def}
|
\stindex{def}
|
||||||
|
|
|
@ -70,7 +70,7 @@ global_stmt: 'global' NAME (',' NAME)*
|
||||||
exec_stmt: 'exec' expr ['in' test [',' test]]
|
exec_stmt: 'exec' expr ['in' test [',' test]]
|
||||||
assert_stmt: 'assert' 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]
|
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
|
||||||
while_stmt: 'while' test ':' suite ['else' ':' suite]
|
while_stmt: 'while' test ':' suite ['else' ':' suite]
|
||||||
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
|
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
|
||||||
|
@ -79,6 +79,8 @@ try_stmt: ('try' ':' suite
|
||||||
['else' ':' suite]
|
['else' ':' suite]
|
||||||
['finally' ':' suite] |
|
['finally' ':' 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
|
# NB compile.c makes sure that the default except clause is last
|
||||||
except_clause: 'except' [test [',' test]]
|
except_clause: 'except' [test [',' test]]
|
||||||
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
|
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
|
||||||
|
|
|
@ -61,11 +61,11 @@ struct _mod {
|
||||||
struct _stmt {
|
struct _stmt {
|
||||||
enum { FunctionDef_kind=1, ClassDef_kind=2, Return_kind=3,
|
enum { FunctionDef_kind=1, ClassDef_kind=2, Return_kind=3,
|
||||||
Delete_kind=4, Assign_kind=5, AugAssign_kind=6, Print_kind=7,
|
Delete_kind=4, Assign_kind=5, AugAssign_kind=6, Print_kind=7,
|
||||||
For_kind=8, While_kind=9, If_kind=10, Raise_kind=11,
|
For_kind=8, While_kind=9, If_kind=10, With_kind=11,
|
||||||
TryExcept_kind=12, TryFinally_kind=13, Assert_kind=14,
|
Raise_kind=12, TryExcept_kind=13, TryFinally_kind=14,
|
||||||
Import_kind=15, ImportFrom_kind=16, Exec_kind=17,
|
Assert_kind=15, Import_kind=16, ImportFrom_kind=17,
|
||||||
Global_kind=18, Expr_kind=19, Pass_kind=20, Break_kind=21,
|
Exec_kind=18, Global_kind=19, Expr_kind=20, Pass_kind=21,
|
||||||
Continue_kind=22 } kind;
|
Break_kind=22, Continue_kind=23 } kind;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
identifier name;
|
identifier name;
|
||||||
|
@ -124,6 +124,12 @@ struct _stmt {
|
||||||
asdl_seq *orelse;
|
asdl_seq *orelse;
|
||||||
} If;
|
} If;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
expr_ty context_expr;
|
||||||
|
expr_ty optional_vars;
|
||||||
|
asdl_seq *body;
|
||||||
|
} With;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
expr_ty type;
|
expr_ty type;
|
||||||
expr_ty inst;
|
expr_ty inst;
|
||||||
|
@ -355,6 +361,8 @@ stmt_ty While(expr_ty test, asdl_seq * body, asdl_seq * orelse, int lineno,
|
||||||
PyArena *arena);
|
PyArena *arena);
|
||||||
stmt_ty If(expr_ty test, asdl_seq * body, asdl_seq * orelse, int lineno,
|
stmt_ty If(expr_ty test, asdl_seq * body, asdl_seq * orelse, int lineno,
|
||||||
PyArena *arena);
|
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
|
stmt_ty Raise(expr_ty type, expr_ty inst, expr_ty tback, int lineno, PyArena
|
||||||
*arena);
|
*arena);
|
||||||
stmt_ty TryExcept(asdl_seq * body, asdl_seq * handlers, asdl_seq * orelse, int
|
stmt_ty TryExcept(asdl_seq * body, asdl_seq * handlers, asdl_seq * orelse, int
|
||||||
|
|
|
@ -38,45 +38,47 @@
|
||||||
#define while_stmt 293
|
#define while_stmt 293
|
||||||
#define for_stmt 294
|
#define for_stmt 294
|
||||||
#define try_stmt 295
|
#define try_stmt 295
|
||||||
#define except_clause 296
|
#define with_stmt 296
|
||||||
#define suite 297
|
#define with_var 297
|
||||||
#define testlist_safe 298
|
#define except_clause 298
|
||||||
#define old_test 299
|
#define suite 299
|
||||||
#define old_lambdef 300
|
#define testlist_safe 300
|
||||||
#define test 301
|
#define old_test 301
|
||||||
#define or_test 302
|
#define old_lambdef 302
|
||||||
#define and_test 303
|
#define test 303
|
||||||
#define not_test 304
|
#define or_test 304
|
||||||
#define comparison 305
|
#define and_test 305
|
||||||
#define comp_op 306
|
#define not_test 306
|
||||||
#define expr 307
|
#define comparison 307
|
||||||
#define xor_expr 308
|
#define comp_op 308
|
||||||
#define and_expr 309
|
#define expr 309
|
||||||
#define shift_expr 310
|
#define xor_expr 310
|
||||||
#define arith_expr 311
|
#define and_expr 311
|
||||||
#define term 312
|
#define shift_expr 312
|
||||||
#define factor 313
|
#define arith_expr 313
|
||||||
#define power 314
|
#define term 314
|
||||||
#define atom 315
|
#define factor 315
|
||||||
#define listmaker 316
|
#define power 316
|
||||||
#define testlist_gexp 317
|
#define atom 317
|
||||||
#define lambdef 318
|
#define listmaker 318
|
||||||
#define trailer 319
|
#define testlist_gexp 319
|
||||||
#define subscriptlist 320
|
#define lambdef 320
|
||||||
#define subscript 321
|
#define trailer 321
|
||||||
#define sliceop 322
|
#define subscriptlist 322
|
||||||
#define exprlist 323
|
#define subscript 323
|
||||||
#define testlist 324
|
#define sliceop 324
|
||||||
#define dictmaker 325
|
#define exprlist 325
|
||||||
#define classdef 326
|
#define testlist 326
|
||||||
#define arglist 327
|
#define dictmaker 327
|
||||||
#define argument 328
|
#define classdef 328
|
||||||
#define list_iter 329
|
#define arglist 329
|
||||||
#define list_for 330
|
#define argument 330
|
||||||
#define list_if 331
|
#define list_iter 331
|
||||||
#define gen_iter 332
|
#define list_for 332
|
||||||
#define gen_for 333
|
#define list_if 333
|
||||||
#define gen_if 334
|
#define gen_iter 334
|
||||||
#define testlist1 335
|
#define gen_for 335
|
||||||
#define encoding_decl 336
|
#define gen_if 336
|
||||||
#define yield_expr 337
|
#define testlist1 337
|
||||||
|
#define encoding_decl 338
|
||||||
|
#define yield_expr 339
|
||||||
|
|
|
@ -72,13 +72,12 @@ extern "C" {
|
||||||
#define INPLACE_XOR 78
|
#define INPLACE_XOR 78
|
||||||
#define INPLACE_OR 79
|
#define INPLACE_OR 79
|
||||||
#define BREAK_LOOP 80
|
#define BREAK_LOOP 80
|
||||||
|
#define WITH_CLEANUP 81
|
||||||
#define LOAD_LOCALS 82
|
#define LOAD_LOCALS 82
|
||||||
#define RETURN_VALUE 83
|
#define RETURN_VALUE 83
|
||||||
#define IMPORT_STAR 84
|
#define IMPORT_STAR 84
|
||||||
#define EXEC_STMT 85
|
#define EXEC_STMT 85
|
||||||
#define YIELD_VALUE 86
|
#define YIELD_VALUE 86
|
||||||
|
|
||||||
#define POP_BLOCK 87
|
#define POP_BLOCK 87
|
||||||
#define END_FINALLY 88
|
#define END_FINALLY 88
|
||||||
#define BUILD_CLASS 89
|
#define BUILD_CLASS 89
|
||||||
|
|
|
@ -553,7 +553,7 @@ class Function(Node):
|
||||||
self.varargs = 1
|
self.varargs = 1
|
||||||
if flags & CO_VARKEYWORDS:
|
if flags & CO_VARKEYWORDS:
|
||||||
self.kwargs = 1
|
self.kwargs = 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def getChildren(self):
|
def getChildren(self):
|
||||||
|
@ -584,7 +584,7 @@ class GenExpr(Node):
|
||||||
self.lineno = lineno
|
self.lineno = lineno
|
||||||
self.argnames = ['[outmost-iterable]']
|
self.argnames = ['[outmost-iterable]']
|
||||||
self.varargs = self.kwargs = None
|
self.varargs = self.kwargs = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def getChildren(self):
|
def getChildren(self):
|
||||||
|
@ -763,7 +763,7 @@ class Lambda(Node):
|
||||||
self.varargs = 1
|
self.varargs = 1
|
||||||
if flags & CO_VARKEYWORDS:
|
if flags & CO_VARKEYWORDS:
|
||||||
self.kwargs = 1
|
self.kwargs = 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def getChildren(self):
|
def getChildren(self):
|
||||||
|
@ -1297,6 +1297,31 @@ class While(Node):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "While(%s, %s, %s)" % (repr(self.test), repr(self.body), repr(self.else_))
|
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):
|
class Yield(Node):
|
||||||
def __init__(self, value, lineno=None):
|
def __init__(self, value, lineno=None):
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
|
@ -41,6 +41,7 @@ def jabs_op(name, op):
|
||||||
hasjabs.append(op)
|
hasjabs.append(op)
|
||||||
|
|
||||||
# Instruction opcodes for compiled code
|
# Instruction opcodes for compiled code
|
||||||
|
# Blank lines correspond to available opcodes
|
||||||
|
|
||||||
def_op('STOP_CODE', 0)
|
def_op('STOP_CODE', 0)
|
||||||
def_op('POP_TOP', 1)
|
def_op('POP_TOP', 1)
|
||||||
|
@ -59,7 +60,6 @@ def_op('UNARY_INVERT', 15)
|
||||||
|
|
||||||
def_op('LIST_APPEND', 18)
|
def_op('LIST_APPEND', 18)
|
||||||
def_op('BINARY_POWER', 19)
|
def_op('BINARY_POWER', 19)
|
||||||
|
|
||||||
def_op('BINARY_MULTIPLY', 20)
|
def_op('BINARY_MULTIPLY', 20)
|
||||||
def_op('BINARY_DIVIDE', 21)
|
def_op('BINARY_DIVIDE', 21)
|
||||||
def_op('BINARY_MODULO', 22)
|
def_op('BINARY_MODULO', 22)
|
||||||
|
@ -70,7 +70,6 @@ def_op('BINARY_FLOOR_DIVIDE', 26)
|
||||||
def_op('BINARY_TRUE_DIVIDE', 27)
|
def_op('BINARY_TRUE_DIVIDE', 27)
|
||||||
def_op('INPLACE_FLOOR_DIVIDE', 28)
|
def_op('INPLACE_FLOOR_DIVIDE', 28)
|
||||||
def_op('INPLACE_TRUE_DIVIDE', 29)
|
def_op('INPLACE_TRUE_DIVIDE', 29)
|
||||||
|
|
||||||
def_op('SLICE+0', 30)
|
def_op('SLICE+0', 30)
|
||||||
def_op('SLICE+1', 31)
|
def_op('SLICE+1', 31)
|
||||||
def_op('SLICE+2', 32)
|
def_op('SLICE+2', 32)
|
||||||
|
@ -93,7 +92,6 @@ def_op('INPLACE_DIVIDE', 58)
|
||||||
def_op('INPLACE_MODULO', 59)
|
def_op('INPLACE_MODULO', 59)
|
||||||
def_op('STORE_SUBSCR', 60)
|
def_op('STORE_SUBSCR', 60)
|
||||||
def_op('DELETE_SUBSCR', 61)
|
def_op('DELETE_SUBSCR', 61)
|
||||||
|
|
||||||
def_op('BINARY_LSHIFT', 62)
|
def_op('BINARY_LSHIFT', 62)
|
||||||
def_op('BINARY_RSHIFT', 63)
|
def_op('BINARY_RSHIFT', 63)
|
||||||
def_op('BINARY_AND', 64)
|
def_op('BINARY_AND', 64)
|
||||||
|
@ -113,13 +111,12 @@ def_op('INPLACE_AND', 77)
|
||||||
def_op('INPLACE_XOR', 78)
|
def_op('INPLACE_XOR', 78)
|
||||||
def_op('INPLACE_OR', 79)
|
def_op('INPLACE_OR', 79)
|
||||||
def_op('BREAK_LOOP', 80)
|
def_op('BREAK_LOOP', 80)
|
||||||
|
def_op('WITH_CLEANUP', 81)
|
||||||
def_op('LOAD_LOCALS', 82)
|
def_op('LOAD_LOCALS', 82)
|
||||||
def_op('RETURN_VALUE', 83)
|
def_op('RETURN_VALUE', 83)
|
||||||
def_op('IMPORT_STAR', 84)
|
def_op('IMPORT_STAR', 84)
|
||||||
def_op('EXEC_STMT', 85)
|
def_op('EXEC_STMT', 85)
|
||||||
def_op('YIELD_VALUE', 86)
|
def_op('YIELD_VALUE', 86)
|
||||||
|
|
||||||
def_op('POP_BLOCK', 87)
|
def_op('POP_BLOCK', 87)
|
||||||
def_op('END_FINALLY', 88)
|
def_op('END_FINALLY', 88)
|
||||||
def_op('BUILD_CLASS', 89)
|
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('CALL_FUNCTION', 131) # #args + (#kwargs << 8)
|
||||||
def_op('MAKE_FUNCTION', 132) # Number of args with default values
|
def_op('MAKE_FUNCTION', 132) # Number of args with default values
|
||||||
def_op('BUILD_SLICE', 133) # Number of items
|
def_op('BUILD_SLICE', 133) # Number of items
|
||||||
|
|
||||||
def_op('MAKE_CLOSURE', 134)
|
def_op('MAKE_CLOSURE', 134)
|
||||||
def_op('LOAD_CLOSURE', 135)
|
def_op('LOAD_CLOSURE', 135)
|
||||||
hasfree.append(135)
|
hasfree.append(135)
|
||||||
|
@ -183,7 +179,6 @@ hasfree.append(137)
|
||||||
def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8)
|
def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8)
|
||||||
def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8)
|
def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8)
|
||||||
def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8)
|
def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8)
|
||||||
|
|
||||||
def_op('EXTENDED_ARG', 143)
|
def_op('EXTENDED_ARG', 143)
|
||||||
EXTENDED_ARG = 143
|
EXTENDED_ARG = 143
|
||||||
|
|
||||||
|
|
|
@ -50,48 +50,50 @@ if_stmt = 292
|
||||||
while_stmt = 293
|
while_stmt = 293
|
||||||
for_stmt = 294
|
for_stmt = 294
|
||||||
try_stmt = 295
|
try_stmt = 295
|
||||||
except_clause = 296
|
with_stmt = 296
|
||||||
suite = 297
|
with_var = 297
|
||||||
testlist_safe = 298
|
except_clause = 298
|
||||||
old_test = 299
|
suite = 299
|
||||||
old_lambdef = 300
|
testlist_safe = 300
|
||||||
test = 301
|
old_test = 301
|
||||||
or_test = 302
|
old_lambdef = 302
|
||||||
and_test = 303
|
test = 303
|
||||||
not_test = 304
|
or_test = 304
|
||||||
comparison = 305
|
and_test = 305
|
||||||
comp_op = 306
|
not_test = 306
|
||||||
expr = 307
|
comparison = 307
|
||||||
xor_expr = 308
|
comp_op = 308
|
||||||
and_expr = 309
|
expr = 309
|
||||||
shift_expr = 310
|
xor_expr = 310
|
||||||
arith_expr = 311
|
and_expr = 311
|
||||||
term = 312
|
shift_expr = 312
|
||||||
factor = 313
|
arith_expr = 313
|
||||||
power = 314
|
term = 314
|
||||||
atom = 315
|
factor = 315
|
||||||
listmaker = 316
|
power = 316
|
||||||
testlist_gexp = 317
|
atom = 317
|
||||||
lambdef = 318
|
listmaker = 318
|
||||||
trailer = 319
|
testlist_gexp = 319
|
||||||
subscriptlist = 320
|
lambdef = 320
|
||||||
subscript = 321
|
trailer = 321
|
||||||
sliceop = 322
|
subscriptlist = 322
|
||||||
exprlist = 323
|
subscript = 323
|
||||||
testlist = 324
|
sliceop = 324
|
||||||
dictmaker = 325
|
exprlist = 325
|
||||||
classdef = 326
|
testlist = 326
|
||||||
arglist = 327
|
dictmaker = 327
|
||||||
argument = 328
|
classdef = 328
|
||||||
list_iter = 329
|
arglist = 329
|
||||||
list_for = 330
|
argument = 330
|
||||||
list_if = 331
|
list_iter = 331
|
||||||
gen_iter = 332
|
list_for = 332
|
||||||
gen_for = 333
|
list_if = 333
|
||||||
gen_if = 334
|
gen_iter = 334
|
||||||
testlist1 = 335
|
gen_for = 335
|
||||||
encoding_decl = 336
|
gen_if = 336
|
||||||
yield_expr = 337
|
testlist1 = 337
|
||||||
|
encoding_decl = 338
|
||||||
|
yield_expr = 339
|
||||||
#--end constants--
|
#--end constants--
|
||||||
|
|
||||||
sym_name = {}
|
sym_name = {}
|
||||||
|
|
34
Lib/test/contextmanager.py
Normal file
34
Lib/test/contextmanager.py
Normal 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
41
Lib/test/nested.py
Normal 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
560
Lib/test/test_with.py
Normal 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()
|
|
@ -62,6 +62,7 @@ Dominic Binks
|
||||||
Philippe Biondi
|
Philippe Biondi
|
||||||
Stuart Bishop
|
Stuart Bishop
|
||||||
Roy Bixler
|
Roy Bixler
|
||||||
|
Mike Bland
|
||||||
Martin Bless
|
Martin Bless
|
||||||
Pablo Bleyer
|
Pablo Bleyer
|
||||||
Erik van Blokland
|
Erik van Blokland
|
||||||
|
|
|
@ -19,6 +19,8 @@ Core and builtins
|
||||||
- dict.__getitem__ now looks for a __missing__ hook before raising
|
- dict.__getitem__ now looks for a __missing__ hook before raising
|
||||||
KeyError.
|
KeyError.
|
||||||
|
|
||||||
|
- PEP 343: with statement implemented.
|
||||||
|
|
||||||
- Fix the encodings package codec search function to only search
|
- Fix the encodings package codec search function to only search
|
||||||
inside its own package. Fixes problem reported in patch #1433198.
|
inside its own package. Fixes problem reported in patch #1433198.
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ module Python
|
||||||
| For(expr target, expr iter, stmt* body, stmt* orelse)
|
| For(expr target, expr iter, stmt* body, stmt* orelse)
|
||||||
| While(expr test, stmt* body, stmt* orelse)
|
| While(expr test, stmt* body, stmt* orelse)
|
||||||
| If(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
|
-- 'type' is a bad name
|
||||||
| Raise(expr? type, expr? inst, expr? tback)
|
| Raise(expr? type, expr? inst, expr? tback)
|
||||||
|
|
|
@ -84,6 +84,12 @@ char *If_fields[]={
|
||||||
"body",
|
"body",
|
||||||
"orelse",
|
"orelse",
|
||||||
};
|
};
|
||||||
|
PyTypeObject *With_type;
|
||||||
|
char *With_fields[]={
|
||||||
|
"context_expr",
|
||||||
|
"optional_vars",
|
||||||
|
"body",
|
||||||
|
};
|
||||||
PyTypeObject *Raise_type;
|
PyTypeObject *Raise_type;
|
||||||
char *Raise_fields[]={
|
char *Raise_fields[]={
|
||||||
"type",
|
"type",
|
||||||
|
@ -465,6 +471,8 @@ static int init_types(void)
|
||||||
if (!While_type) return 0;
|
if (!While_type) return 0;
|
||||||
If_type = make_type("If", stmt_type, If_fields, 3);
|
If_type = make_type("If", stmt_type, If_fields, 3);
|
||||||
if (!If_type) return 0;
|
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);
|
Raise_type = make_type("Raise", stmt_type, Raise_fields, 3);
|
||||||
if (!Raise_type) return 0;
|
if (!Raise_type) return 0;
|
||||||
TryExcept_type = make_type("TryExcept", stmt_type, TryExcept_fields, 3);
|
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;
|
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
|
stmt_ty
|
||||||
Raise(expr_ty type, expr_ty inst, expr_ty tback, int lineno, PyArena *arena)
|
Raise(expr_ty type, expr_ty inst, expr_ty tback, int lineno, PyArena *arena)
|
||||||
{
|
{
|
||||||
|
@ -2062,6 +2093,26 @@ ast2obj_stmt(void* _o)
|
||||||
goto failed;
|
goto failed;
|
||||||
Py_DECREF(value);
|
Py_DECREF(value);
|
||||||
break;
|
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:
|
case Raise_kind:
|
||||||
result = PyType_GenericNew(Raise_type, NULL, NULL);
|
result = PyType_GenericNew(Raise_type, NULL, NULL);
|
||||||
if (!result) goto failed;
|
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, "For", (PyObject*)For_type) < 0) return;
|
||||||
if(PyDict_SetItemString(d, "While", (PyObject*)While_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, "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, "Raise", (PyObject*)Raise_type) < 0) return;
|
||||||
if(PyDict_SetItemString(d, "TryExcept", (PyObject*)TryExcept_type) < 0)
|
if(PyDict_SetItemString(d, "TryExcept", (PyObject*)TryExcept_type) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
46
Python/ast.c
46
Python/ast.c
|
@ -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"
|
Only sets context for expr kinds that "can appear in assignment context"
|
||||||
(according to ../Parser/Python.asdl). For other expr kinds, it sets
|
(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
|
a little more complex than necessary as a result. It also means
|
||||||
that expressions in an augmented assignment have no context.
|
that expressions in an augmented assignment have no context.
|
||||||
Consider restructuring so that augmented assignment uses
|
Consider restructuring so that augmented assignment uses
|
||||||
set_context(), too
|
set_context(), too.
|
||||||
*/
|
*/
|
||||||
assert(ctx != AugStore && ctx != AugLoad);
|
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);
|
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
|
static stmt_ty
|
||||||
ast_for_classdef(struct compiling *c, const node *n)
|
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);
|
return ast_for_for_stmt(c, ch);
|
||||||
case try_stmt:
|
case try_stmt:
|
||||||
return ast_for_try_stmt(c, ch);
|
return ast_for_try_stmt(c, ch);
|
||||||
|
case with_stmt:
|
||||||
|
return ast_for_with_stmt(c, ch);
|
||||||
case funcdef:
|
case funcdef:
|
||||||
return ast_for_funcdef(c, ch);
|
return ast_for_funcdef(c, ch);
|
||||||
case classdef:
|
case classdef:
|
||||||
|
|
|
@ -97,6 +97,7 @@ static PyObject *load_args(PyObject ***, int);
|
||||||
#define CALL_FLAG_KW 2
|
#define CALL_FLAG_KW 2
|
||||||
|
|
||||||
#ifdef LLTRACE
|
#ifdef LLTRACE
|
||||||
|
static int lltrace;
|
||||||
static int prtrace(PyObject *, char *);
|
static int prtrace(PyObject *, char *);
|
||||||
#endif
|
#endif
|
||||||
static int call_trace(Py_tracefunc, PyObject *, PyFrameObject *,
|
static int call_trace(Py_tracefunc, PyObject *, PyFrameObject *,
|
||||||
|
@ -540,9 +541,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw)
|
||||||
unsigned char *first_instr;
|
unsigned char *first_instr;
|
||||||
PyObject *names;
|
PyObject *names;
|
||||||
PyObject *consts;
|
PyObject *consts;
|
||||||
#ifdef LLTRACE
|
|
||||||
int lltrace;
|
|
||||||
#endif
|
|
||||||
#if defined(Py_DEBUG) || defined(LLTRACE)
|
#if defined(Py_DEBUG) || defined(LLTRACE)
|
||||||
/* Make it easier to find out where we are with a debugger */
|
/* Make it easier to find out where we are with a debugger */
|
||||||
char *filename;
|
char *filename;
|
||||||
|
@ -661,10 +659,12 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw)
|
||||||
#define STACKADJ(n) { (void)(BASIC_STACKADJ(n), \
|
#define STACKADJ(n) { (void)(BASIC_STACKADJ(n), \
|
||||||
lltrace && prtrace(TOP(), "stackadj")); \
|
lltrace && prtrace(TOP(), "stackadj")); \
|
||||||
assert(STACK_LEVEL() <= f->f_stacksize); }
|
assert(STACK_LEVEL() <= f->f_stacksize); }
|
||||||
|
#define EXT_POP(STACK_POINTER) (lltrace && prtrace(*(STACK_POINTER), "ext_pop"), *--(STACK_POINTER))
|
||||||
#else
|
#else
|
||||||
#define PUSH(v) BASIC_PUSH(v)
|
#define PUSH(v) BASIC_PUSH(v)
|
||||||
#define POP() BASIC_POP()
|
#define POP() BASIC_POP()
|
||||||
#define STACKADJ(n) BASIC_STACKADJ(n)
|
#define STACKADJ(n) BASIC_STACKADJ(n)
|
||||||
|
#define EXT_POP(STACK_POINTER) (*--(STACK_POINTER))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Local variable macros */
|
/* Local variable macros */
|
||||||
|
@ -2172,6 +2172,43 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw)
|
||||||
STACK_LEVEL());
|
STACK_LEVEL());
|
||||||
continue;
|
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:
|
case CALL_FUNCTION:
|
||||||
{
|
{
|
||||||
PyObject **sp;
|
PyObject **sp;
|
||||||
|
@ -2511,9 +2548,9 @@ fast_yield:
|
||||||
return retval;
|
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
|
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 *
|
PyObject *
|
||||||
PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
|
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
|
static void
|
||||||
err_args(PyObject *func, int flags, int nargs)
|
err_args(PyObject *func, int flags, int nargs)
|
||||||
{
|
{
|
||||||
|
|
166
Python/compile.c
166
Python/compile.c
|
@ -191,6 +191,8 @@ static void compiler_pop_fblock(struct compiler *, enum fblocktype,
|
||||||
static int inplace_binop(struct compiler *, operator_ty);
|
static int inplace_binop(struct compiler *, operator_ty);
|
||||||
static int expr_constant(expr_ty e);
|
static int expr_constant(expr_ty e);
|
||||||
|
|
||||||
|
static int compiler_with(struct compiler *, stmt_ty);
|
||||||
|
|
||||||
static PyCodeObject *assemble(struct compiler *, int addNone);
|
static PyCodeObject *assemble(struct compiler *, int addNone);
|
||||||
static PyObject *__doc__;
|
static PyObject *__doc__;
|
||||||
|
|
||||||
|
@ -289,6 +291,7 @@ PyAST_Compile(mod_ty mod, const char *filename, PyCompilerFlags *flags,
|
||||||
|
|
||||||
error:
|
error:
|
||||||
compiler_free(&c);
|
compiler_free(&c);
|
||||||
|
assert(!PyErr_Occurred());
|
||||||
return co;
|
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.
|
/* Allocate a new block and return a pointer to it.
|
||||||
Returns NULL on error.
|
Returns NULL on error.
|
||||||
*/
|
*/
|
||||||
|
@ -1360,7 +1375,8 @@ opcode_stack_effect(int opcode, int oparg)
|
||||||
return -1;
|
return -1;
|
||||||
case BREAK_LOOP:
|
case BREAK_LOOP:
|
||||||
return 0;
|
return 0;
|
||||||
|
case WITH_CLEANUP:
|
||||||
|
return 3;
|
||||||
case LOAD_LOCALS:
|
case LOAD_LOCALS:
|
||||||
return 1;
|
return 1;
|
||||||
case RETURN_VALUE:
|
case RETURN_VALUE:
|
||||||
|
@ -2663,6 +2679,8 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
|
||||||
break;
|
break;
|
||||||
case Continue_kind:
|
case Continue_kind:
|
||||||
return compiler_continue(c);
|
return compiler_continue(c);
|
||||||
|
case With_kind:
|
||||||
|
return compiler_with(c, s);
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -3124,7 +3142,6 @@ compiler_listcomp_generator(struct compiler *c, PyObject *tmpname,
|
||||||
static int
|
static int
|
||||||
compiler_listcomp(struct compiler *c, expr_ty e)
|
compiler_listcomp(struct compiler *c, expr_ty e)
|
||||||
{
|
{
|
||||||
char tmpname[256];
|
|
||||||
identifier tmp;
|
identifier tmp;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
static identifier append;
|
static identifier append;
|
||||||
|
@ -3136,8 +3153,7 @@ compiler_listcomp(struct compiler *c, expr_ty e)
|
||||||
if (!append)
|
if (!append)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
PyOS_snprintf(tmpname, sizeof(tmpname), "_[%d]", ++c->u->u_tmpname);
|
tmp = compiler_new_tmpname(c);
|
||||||
tmp = PyString_FromString(tmpname);
|
|
||||||
if (!tmp)
|
if (!tmp)
|
||||||
return 0;
|
return 0;
|
||||||
ADDOP_I(c, BUILD_LIST, 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
|
static int
|
||||||
compiler_visit_expr(struct compiler *c, expr_ty e)
|
compiler_visit_expr(struct compiler *c, expr_ty e)
|
||||||
{
|
{
|
||||||
|
|
1419
Python/graminit.c
1419
Python/graminit.c
File diff suppressed because it is too large
Load diff
|
@ -54,9 +54,10 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *);
|
||||||
Python 2.4b1: 62061
|
Python 2.4b1: 62061
|
||||||
Python 2.5a0: 62071
|
Python 2.5a0: 62071
|
||||||
Python 2.5a0: 62081 (ast-branch)
|
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
|
/* Magic word as global; note that _PyImport_Init() can change the
|
||||||
value of this global to accommodate for alterations of how the
|
value of this global to accommodate for alterations of how the
|
||||||
|
|
|
@ -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
|
static int
|
||||||
symtable_visit_stmt(struct symtable *st, stmt_ty s)
|
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:
|
case Continue_kind:
|
||||||
/* nothing to do here */
|
/* nothing to do here */
|
||||||
break;
|
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;
|
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.keys);
|
||||||
VISIT_SEQ(st, expr, e->v.Dict.values);
|
VISIT_SEQ(st, expr, e->v.Dict.values);
|
||||||
break;
|
break;
|
||||||
case ListComp_kind: {
|
case ListComp_kind:
|
||||||
char tmpname[256];
|
if (!symtable_new_tmpname(st))
|
||||||
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;
|
return 0;
|
||||||
Py_DECREF(tmp);
|
|
||||||
VISIT(st, expr, e->v.ListComp.elt);
|
VISIT(st, expr, e->v.ListComp.elt);
|
||||||
VISIT_SEQ(st, comprehension, e->v.ListComp.generators);
|
VISIT_SEQ(st, comprehension, e->v.ListComp.generators);
|
||||||
break;
|
break;
|
||||||
}
|
case GeneratorExp_kind:
|
||||||
case GeneratorExp_kind: {
|
if (!symtable_visit_genexp(st, e))
|
||||||
if (!symtable_visit_genexp(st, e)) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case Yield_kind:
|
case Yield_kind:
|
||||||
if (e->v.Yield.value)
|
if (e->v.Yield.value)
|
||||||
VISIT(st, expr, e->v.Yield.value);
|
VISIT(st, expr, e->v.Yield.value);
|
||||||
|
|
|
@ -20,6 +20,7 @@ Break:
|
||||||
Continue:
|
Continue:
|
||||||
For: assign, list, body, else_&
|
For: assign, list, body, else_&
|
||||||
While: test, body, else_&
|
While: test, body, else_&
|
||||||
|
With: expr, vars&, body
|
||||||
If: tests!, else_&
|
If: tests!, else_&
|
||||||
Exec: expr, locals&, globals&
|
Exec: expr, locals&, globals&
|
||||||
From: modname*, names*
|
From: modname*, names*
|
||||||
|
@ -42,7 +43,7 @@ AssAttr: expr, attrname*, flags*
|
||||||
ListComp: expr, quals!
|
ListComp: expr, quals!
|
||||||
ListCompFor: assign, list, ifs!
|
ListCompFor: assign, list, ifs!
|
||||||
ListCompIf: test
|
ListCompIf: test
|
||||||
GenExpr: code
|
GenExpr: code
|
||||||
GenExprInner: expr, quals!
|
GenExprInner: expr, quals!
|
||||||
GenExprFor: assign, iter, ifs!
|
GenExprFor: assign, iter, ifs!
|
||||||
GenExprIf: test
|
GenExprIf: test
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue