log = []
class Tracked:
def __init__(self, name):
self.name = name
def __enter__(self):
log.append('enter ' + self.name)
return self
def __exit__(self, typ, val, tb):
log.append('exit ' + self.name)
return None
with Tracked('a') as bound:
log.append('body')
assert bound.name == 'a'
assert log == ['enter a', 'body', 'exit a']
class EnterValue:
def __enter__(self):
return 42
def __exit__(self, typ, val, tb):
return None
with EnterValue() as v:
assert v == 42
class Grab:
def __enter__(self):
return self
def __exit__(self, typ, val, tb):
self.typ = typ
self.val = val
self.tb = tb
return True
g = Grab()
with g:
pass
assert g.typ is None
assert g.val is None
assert g.tb is None
g = Grab()
with g:
raise ValueError('boom')
assert g.typ is ValueError
assert isinstance(g.val, ValueError)
assert str(g.val) == 'boom'
swallowed = False
with Grab():
raise ValueError('to-swallow')
swallowed = False swallowed = True
assert swallowed
class TruthyStr:
def __enter__(self):
return self
def __exit__(self, typ, val, tb):
return 'truthy'
with TruthyStr():
raise ValueError('swallowed by str')
class Passthrough:
def __enter__(self):
return self
def __exit__(self, typ, val, tb):
return None
caught = None
try:
with Passthrough():
raise ValueError('passthrough-prop')
except ValueError as e:
caught = str(e)
assert caught == 'passthrough-prop'
with Grab():
inside = 'ran'
assert inside == 'ran'
class BadEnter:
def __enter__(self):
raise ValueError('no-entry')
def __exit__(self, typ, val, tb):
return True
ran_body = False
caught = None
try:
with BadEnter():
ran_body = True
except ValueError as e:
caught = str(e)
assert caught == 'no-entry'
assert not ran_body, 'body skipped when __enter__ raises'
class BadExit:
def __enter__(self):
return self
def __exit__(self, typ, val, tb):
raise ValueError('cleanup-failed')
caught = None
try:
with BadExit():
pass
except ValueError as e:
caught = str(e)
assert caught == 'cleanup-failed'
caught_info = None
try:
with BadExit():
raise RuntimeError('original')
except ValueError as e:
caught_info = ('ValueError', str(e))
except RuntimeError as e:
caught_info = ('RuntimeError', str(e))
assert caught_info == ('ValueError', 'cleanup-failed')
class NoEnter:
def __exit__(self, typ, val, tb):
return None
class NoExit:
def __enter__(self):
return self
class Neither:
pass
caught = None
try:
with NoEnter():
pass
except TypeError as e:
caught = str(e)
assert caught == "'NoEnter' object does not support the context manager protocol (missed __enter__ method)"
caught = None
try:
with NoExit():
pass
except TypeError as e:
caught = str(e)
assert caught == "'NoExit' object does not support the context manager protocol (missed __exit__ method)"
caught = None
try:
with Neither():
pass
except TypeError as e:
caught = str(e)
assert caught == "'Neither' object does not support the context manager protocol (missed __exit__ method)"
class IntEnter:
__enter__ = 5
def __exit__(self, typ, val, tb):
return None
caught = None
try:
with IntEnter():
pass
except TypeError as e:
caught = str(e)
assert caught == "'int' object is not callable"
DynCm = type('DynCm', (), {'__enter__': lambda self: 'dyn', '__exit__': lambda self, typ, val, tb: None})
with DynCm() as v:
assert v == 'dyn'
p = Passthrough()
assert p.__enter__() is p
assert p.__exit__(None, None, None) is None
assert Grab().__exit__(ValueError, ValueError('x'), None) is True
class Shadow:
def __enter__(self):
return 'class-enter'
def __exit__(self, typ, val, tb):
return None
def instance_enter():
return 'instance-enter'
s = Shadow()
s.__enter__ = instance_enter
with s as v:
assert v == 'class-enter'
assert s.__enter__() == 'instance-enter'
caught = None
try:
with BadExit() as (a, b):
pass
except ValueError as e:
caught = str(e)
except TypeError:
caught = 'unpack-error-uncaught'
assert caught == 'cleanup-failed'
log = []
def early_return():
with Tracked('ret'):
return 'early'
assert early_return() == 'early'
assert log == ['enter ret', 'exit ret']
log = []
for i in range(3):
with Tracked('loop' + str(i)):
if i == 1:
continue
if i == 2:
break
assert log == [
'enter loop0',
'exit loop0',
'enter loop1',
'exit loop1',
'enter loop2',
'exit loop2',
]
log = []
with Tracked('outer'), Tracked('inner'):
log.append('body')
assert log == ['enter outer', 'enter inner', 'body', 'exit inner', 'exit outer']
outcome = None
with Passthrough():
try:
with Grab():
raise ValueError('inner-swallow')
outcome = 'fell-through'
except ValueError:
outcome = 'propagated'
assert outcome == 'fell-through'
caught_info = None
try:
with BadExit():
with Passthrough():
raise RuntimeError('inner-raise')
except ValueError as e:
caught_info = ('ValueError', str(e))
except RuntimeError as e:
caught_info = ('RuntimeError', str(e))
assert caught_info == ('ValueError', 'cleanup-failed')