From: Haoyu Bai Date: Tue, 25 May 2010 17:26:37 +0000 (+0800) Subject: implemented T487 'with' with multiple managers X-Git-Tag: 0.14.alpha0~254^2 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=00b0bb863805c84d8ef0b559824acd5744816f2c;p=cython.git implemented T487 'with' with multiple managers --- diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index 72471e8d..12412942 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -1503,50 +1503,62 @@ def p_include_statement(s, ctx): return Nodes.PassStatNode(pos) def p_with_statement(s): - pos = s.position() s.next() # 'with' -# if s.sy == 'IDENT' and s.systring in ('gil', 'nogil'): + if s.systring == 'template': + node = p_with_template(s) + else: + node = p_with_items(s) + return node + +def p_with_items(s): + pos = s.position() if s.sy == 'IDENT' and s.systring == 'nogil': state = s.systring s.next() - body = p_suite(s) - return Nodes.GILStatNode(pos, state = state, body = body) - elif s.systring == 'template': - templates = [] - s.next() - s.expect('[') - #s.next() - templates.append(s.systring) - s.next() - while s.systring == ',': - s.next() - templates.append(s.systring) - s.next() - s.expect(']') - if s.sy == ':': + if s.sy == ',': s.next() - s.expect_newline("Syntax error in template function declaration") - s.expect_indent() - body_ctx = Ctx() - body_ctx.templates = templates - func_or_var = p_c_func_or_var_declaration(s, pos, body_ctx) - s.expect_dedent() - return func_or_var + body = p_with_items(s) else: - error(pos, "Syntax error in template function declaration") + body = p_suite(s) + return Nodes.GILStatNode(pos, state = state, body = body) else: manager = p_test(s) target = None if s.sy == 'IDENT' and s.systring == 'as': s.next() - allow_multi = (s.sy == '(') - target = p_target(s, ':') - if not allow_multi and isinstance(target, ExprNodes.TupleNode): - s.error("Multiple with statement target values not allowed without paranthesis") - body = p_suite(s) + target = p_starred_expr(s) + if s.sy == ',': + s.next() + body = p_with_items(s) + else: + body = p_suite(s) return Nodes.WithStatNode(pos, manager = manager, target = target, body = body) - + +def p_with_template(s): + pos = s.position() + templates = [] + s.next() + s.expect('[') + templates.append(s.systring) + s.next() + while s.systring == ',': + s.next() + templates.append(s.systring) + s.next() + s.expect(']') + if s.sy == ':': + s.next() + s.expect_newline("Syntax error in template function declaration") + s.expect_indent() + body_ctx = Ctx() + body_ctx.templates = templates + func_or_var = p_c_func_or_var_declaration(s, pos, body_ctx) + s.expect_dedent() + return func_or_var + else: + error(pos, "Syntax error in template function declaration") + def p_simple_statement(s, first_statement = 0): #print "p_simple_statement:", s.sy, s.systring ### if s.sy == 'global': diff --git a/tests/run/withstat.pyx b/tests/run/withstat.pyx index bea6437e..fad2eaff 100644 --- a/tests/run/withstat.pyx +++ b/tests/run/withstat.pyx @@ -118,3 +118,121 @@ def typed(): with c as i: i += 11 print i + +def multimanager(): + """ + >>> multimanager() + enter + enter + enter + enter + enter + enter + 2 + value + 1 2 3 4 5 + nested + exit + exit + exit + exit + exit + exit + """ + with ContextManager(1), ContextManager(2) as x, ContextManager(u'value') as y,\ + ContextManager(3), ContextManager((1, 2, (3, (4, 5)))) as (a, b, (c, (d, e))): + with ContextManager(u'nested') as nested: + print x + print y + print a, b, c, d, e + print nested + +# Tests borrowed from pyregr test_with.py, +# modified to follow the constraints of Cython. +import unittest + +class Dummy(object): + def __init__(self, value=None, gobble=False): + if value is None: + value = self + self.value = value + self.gobble = gobble + self.enter_called = False + self.exit_called = False + + def __enter__(self): + self.enter_called = True + return self.value + + def __exit__(self, *exc_info): + self.exit_called = True + self.exc_info = exc_info + if self.gobble: + return True + +class InitRaises(object): + def __init__(self): raise RuntimeError() + +class EnterRaises(object): + def __enter__(self): raise RuntimeError() + def __exit__(self, *exc_info): pass + +class ExitRaises(object): + def __enter__(self): pass + def __exit__(self, *exc_info): raise RuntimeError() + +class NestedWith(unittest.TestCase): + """ + >>> NestedWith().runTest() + """ + + def runTest(self): + self.testNoExceptions() + self.testExceptionInExprList() + self.testExceptionInEnter() + self.testExceptionInExit() + self.testEnterReturnsTuple() + + def testNoExceptions(self): + with Dummy() as a, Dummy() as b: + self.assertTrue(a.enter_called) + self.assertTrue(b.enter_called) + self.assertTrue(a.exit_called) + self.assertTrue(b.exit_called) + + def testExceptionInExprList(self): + try: + with Dummy() as a, InitRaises(): + pass + except: + pass + self.assertTrue(a.enter_called) + self.assertTrue(a.exit_called) + + def testExceptionInEnter(self): + try: + with Dummy() as a, EnterRaises(): + self.fail('body of bad with executed') + except RuntimeError: + pass + else: + self.fail('RuntimeError not reraised') + self.assertTrue(a.enter_called) + self.assertTrue(a.exit_called) + + def testExceptionInExit(self): + body_executed = False + with Dummy(gobble=True) as a, ExitRaises(): + body_executed = True + self.assertTrue(a.enter_called) + self.assertTrue(a.exit_called) + self.assertTrue(body_executed) + self.assertNotEqual(a.exc_info[0], None) + + def testEnterReturnsTuple(self): + with Dummy(value=(1,2)) as (a1, a2), \ + Dummy(value=(10, 20)) as (b1, b2): + self.assertEquals(1, a1) + self.assertEquals(2, a2) + self.assertEquals(10, b1) + self.assertEquals(20, b2)