withstat.pyx 6.02 KB
Newer Older
1 2
from __future__ import with_statement

Stefan Behnel's avatar
Stefan Behnel committed
3
import sys
4

Stefan Behnel's avatar
Stefan Behnel committed
5
def typename(t):
Stefan Behnel's avatar
Stefan Behnel committed
6 7 8 9 10 11 12
    name = type(t).__name__
    if sys.version_info < (2,5):
        if name == 'classobj' and issubclass(t, MyException):
            name = 'type'
        elif name == 'instance' and isinstance(t, MyException):
            name = 'MyException'
    return u"<type '%s'>" % name
Stefan Behnel's avatar
Stefan Behnel committed
13

14 15 16
class MyException(Exception):
    pass

Stefan Behnel's avatar
Stefan Behnel committed
17
class ContextManager(object):
18 19 20 21
    def __init__(self, value, exit_ret = None):
        self.value = value
        self.exit_ret = exit_ret

22
    def __exit__(self, a, b, tb):
Stefan Behnel's avatar
Stefan Behnel committed
23
        print u"exit", typename(a), typename(b), typename(tb)
24 25 26
        return self.exit_ret
        
    def __enter__(self):
Stefan Behnel's avatar
Stefan Behnel committed
27
        print u"enter"
28
        return self.value
29 30

def no_as():
31 32 33 34 35 36
    """
    >>> no_as()
    enter
    hello
    exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
    """
Stefan Behnel's avatar
Stefan Behnel committed
37 38
    with ContextManager(u"value"):
        print u"hello"
39 40
        
def basic():
41 42 43 44 45 46
    """
    >>> basic()
    enter
    value
    exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
    """
Stefan Behnel's avatar
Stefan Behnel committed
47
    with ContextManager(u"value") as x:
48
        print x
49 50
        
def with_pass():
51 52 53 54 55
    """
    >>> with_pass()
    enter
    exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
    """
56 57 58 59
    with ContextManager(u"value") as x:
        pass
        
def with_return():
60 61 62 63 64
    """
    >>> with_return()
    enter
    exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
    """
65 66 67 68
    with ContextManager(u"value") as x:
        # FIXME: DISABLED - currently crashes!!
        # return x
        pass
69 70

def with_exception(exit_ret):
71 72 73 74 75 76 77 78 79 80 81
    """
    >>> with_exception(None)
    enter
    value
    exit <type 'type'> <type 'MyException'> <type 'traceback'>
    outer except
    >>> with_exception(True)
    enter
    value
    exit <type 'type'> <type 'MyException'> <type 'traceback'>
    """
82
    try:
Stefan Behnel's avatar
Stefan Behnel committed
83
        with ContextManager(u"value", exit_ret=exit_ret) as value:
84
            print value
85
            raise MyException()
86
    except:
Stefan Behnel's avatar
Stefan Behnel committed
87
        print u"outer except"
88 89

def multitarget():
90 91 92 93 94 95
    """
    >>> multitarget()
    enter
    1 2 3 4 5
    exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
    """
96 97 98 99
    with ContextManager((1, 2, (3, (4, 5)))) as (a, b, (c, (d, e))):
        print a, b, c, d, e

def tupletarget():
100 101 102 103 104 105
    """
    >>> tupletarget()
    enter
    (1, 2, (3, (4, 5)))
    exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
    """
106 107
    with ContextManager((1, 2, (3, (4, 5)))) as t:
        print t
108

109
def typed():
110 111 112 113 114 115
    """
    >>> typed()
    enter
    10
    exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
    """
116 117 118 119 120
    cdef unsigned char i
    c = ContextManager(255)
    with c as i:
        i += 11
        print i
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238

def multimanager():
    """
    >>> multimanager()
    enter
    enter
    enter
    enter
    enter
    enter
    2
    value
    1 2 3 4 5
    nested
    exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
    exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
    exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
    exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
    exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
    exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
    """
    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)