Break or exit out of "with" statement?
Question:
I’d just like to exit out of a with
statement under certain conditions:
with open(path) as f:
print 'before condition'
if <condition>: break #syntax error!
print 'after condition'
Of course, the above doesn’t work. Is there a way to do this? (I know that I can invert the condition: if not <condition>: print 'after condition'
— any way that is like above?)
Answers:
Since break
can only occur inside a loop your options are somewhat limited inside the with
to:
- return (put “with” + associated statements inside function)
- exit (bail from program – probably not ideal)
- exception (generate exception inside “with”, catch below)
Having a function and using return
is probably the cleanest and easiest solution here if you can isolate the with
and the associated statements (and nothing else) inside a function.
Otherwise generate an exception inside the with
when needed, catch immediately below/outside the with
to continue the rest of the code.
Update: As OP suggests in the comments below (perhaps tonuge in cheek?) one could also wrap the with
statement inside a loop to get the break
to work – though that would be semantically misleading. So while a working solution, probably not something that would be recommend).
The best way would be to encapsulate it in a function and use return
:
def do_it():
with open(path) as f:
print 'before condition'
if <condition>:
return
print 'after condition'
You could put everything inside a function, and when the condition is true call a return.
f = open("somefile","r")
for line in f.readlines():
if somecondition: break;
f.close()
I dont think you can break out of the with… you need to use a loop…
[edit] or just do the function method others mentioned
I think you should just restructure the logic:
with open(path) as f:
print 'before condition checked'
if not <condition>:
print 'after condition checked'
as shorthand snippet:
class a:
def __enter__(self):
print 'enter'
def __exit__(self ,type, value, traceback):
print 'exit'
for i in [1]:
with a():
print("before")
break
print("after")
…
enter
before
exit
with
giving you trouble? Throw more with
-able objects at the problem!
class fragile(object):
class Break(Exception):
"""Break out of the with statement"""
def __init__(self, value):
self.value = value
def __enter__(self):
return self.value.__enter__()
def __exit__(self, etype, value, traceback):
error = self.value.__exit__(etype, value, traceback)
if etype == self.Break:
return True
return error
Just wrap the expression you’re going to with
with fragile
, and raise fragile.Break
to break out at any point!
with fragile(open(path)) as f:
print 'before condition'
if condition:
raise fragile.Break
print 'after condition'
Benefits of this setup
- Uses
with
and just the with
; doesn’t wrap your function in a semantically misleading one-run ‘loop’ or a narrowly specialized function, and doesn’t force you to do any extra error handling after the with
.
- Keeps your local variables available, instead of having to pass them to a wrapping function.
-
Nestable!
with fragile(open(path1)) as f:
with fragile(open(path2)) as g:
print f.read()
print g.read()
raise fragile.Break
print "This wont happen"
print "This will though!"
This way, you don’t have to create a new function to wrap the outer with
if you want both to break.
- Doesn’t require restructuring at all: just wrap what you already have with
fragile
and you’re good to go!
Downsides of this setup
- Doesn’t actually use a ‘break’ statement. Can’t win em all 😉
This is an ancient question, but this is an application for the handy “breakable scope” idiom. Just imbed your with
statement inside:
for _ in (True,):
with open(path) as f:
print 'before condition'
if <condition>: break
print 'after condition'
This idiom creates a “loop”, always executed exactly once, for the sole purpose of enclosing a block of code inside a scope that can be broken out of conditionally. In OP’s case, it was a context manager invocation to be enclosed, but it could be any bounded sequence of statements that may require conditional escape.
The accepted answer is fine, but this technique does the same thing without needing to create a function, which is not always convenient or desired.
There exists a function __exit__()
for this purpose. Syntax is as follows:
with VAR = EXPR:
try:
BLOCK
finally:
VAR.__exit__()
Change from “break” to “f.close”
with open(path) as f:
print('before condition')
if <condition>: f.close()
print('after condition')
Use while True
:
while True:
with open(path) as f:
print 'before condition'
if <condition>:
break
print 'after condition n'
break
Here is another way of doing it using try
and except
, even though inverting the condition is probably more convenient.
class BreakOut(Exception): pass
try:
with open(path) as f:
print('before condition')
if <condition>:
raise BreakOut #syntax error!
print('after condition')
except BreakOut:
pass
This question was asked before Python 3.4 existed but with 3.4 you can use contextlib.supress
,
suppressing your own personal exception.
See that this (runnable as is) code
from contextlib import suppress
class InterruptWithBlock(UserWarning):
"""To be used to interrupt the march of a with"""
condition = True
with suppress(InterruptWithBlock):
print('before condition')
if condition: raise InterruptWithBlock()
print('after condition')
# Will not print 'after condition` if condition is True.
So with the code in the question, you’d do:
with suppress(InterruptWithBlock) as _, open(path) as f:
print('before condition')
if <condition>: raise InterruptWithBlock()
print('after condition')
Note: If you’re (still) before 3.4, you can still make your own suppress
context manager easily.
I’d just like to exit out of a with
statement under certain conditions:
with open(path) as f:
print 'before condition'
if <condition>: break #syntax error!
print 'after condition'
Of course, the above doesn’t work. Is there a way to do this? (I know that I can invert the condition: if not <condition>: print 'after condition'
— any way that is like above?)
Since break
can only occur inside a loop your options are somewhat limited inside the with
to:
- return (put “with” + associated statements inside function)
- exit (bail from program – probably not ideal)
- exception (generate exception inside “with”, catch below)
Having a function and using return
is probably the cleanest and easiest solution here if you can isolate the with
and the associated statements (and nothing else) inside a function.
Otherwise generate an exception inside the with
when needed, catch immediately below/outside the with
to continue the rest of the code.
Update: As OP suggests in the comments below (perhaps tonuge in cheek?) one could also wrap the with
statement inside a loop to get the break
to work – though that would be semantically misleading. So while a working solution, probably not something that would be recommend).
The best way would be to encapsulate it in a function and use return
:
def do_it():
with open(path) as f:
print 'before condition'
if <condition>:
return
print 'after condition'
You could put everything inside a function, and when the condition is true call a return.
f = open("somefile","r")
for line in f.readlines():
if somecondition: break;
f.close()
I dont think you can break out of the with… you need to use a loop…
[edit] or just do the function method others mentioned
I think you should just restructure the logic:
with open(path) as f:
print 'before condition checked'
if not <condition>:
print 'after condition checked'
as shorthand snippet:
class a:
def __enter__(self):
print 'enter'
def __exit__(self ,type, value, traceback):
print 'exit'
for i in [1]:
with a():
print("before")
break
print("after")
…
enter
before
exit
with
giving you trouble? Throw more with
-able objects at the problem!
class fragile(object):
class Break(Exception):
"""Break out of the with statement"""
def __init__(self, value):
self.value = value
def __enter__(self):
return self.value.__enter__()
def __exit__(self, etype, value, traceback):
error = self.value.__exit__(etype, value, traceback)
if etype == self.Break:
return True
return error
Just wrap the expression you’re going to with
with fragile
, and raise fragile.Break
to break out at any point!
with fragile(open(path)) as f:
print 'before condition'
if condition:
raise fragile.Break
print 'after condition'
Benefits of this setup
- Uses
with
and just thewith
; doesn’t wrap your function in a semantically misleading one-run ‘loop’ or a narrowly specialized function, and doesn’t force you to do any extra error handling after thewith
. - Keeps your local variables available, instead of having to pass them to a wrapping function.
-
Nestable!
with fragile(open(path1)) as f: with fragile(open(path2)) as g: print f.read() print g.read() raise fragile.Break print "This wont happen" print "This will though!"
This way, you don’t have to create a new function to wrap the outer
with
if you want both to break. - Doesn’t require restructuring at all: just wrap what you already have with
fragile
and you’re good to go!
Downsides of this setup
- Doesn’t actually use a ‘break’ statement. Can’t win em all 😉
This is an ancient question, but this is an application for the handy “breakable scope” idiom. Just imbed your with
statement inside:
for _ in (True,):
with open(path) as f:
print 'before condition'
if <condition>: break
print 'after condition'
This idiom creates a “loop”, always executed exactly once, for the sole purpose of enclosing a block of code inside a scope that can be broken out of conditionally. In OP’s case, it was a context manager invocation to be enclosed, but it could be any bounded sequence of statements that may require conditional escape.
The accepted answer is fine, but this technique does the same thing without needing to create a function, which is not always convenient or desired.
There exists a function __exit__()
for this purpose. Syntax is as follows:
with VAR = EXPR:
try:
BLOCK
finally:
VAR.__exit__()
Change from “break” to “f.close”
with open(path) as f:
print('before condition')
if <condition>: f.close()
print('after condition')
Use while True
:
while True:
with open(path) as f:
print 'before condition'
if <condition>:
break
print 'after condition n'
break
Here is another way of doing it using try
and except
, even though inverting the condition is probably more convenient.
class BreakOut(Exception): pass
try:
with open(path) as f:
print('before condition')
if <condition>:
raise BreakOut #syntax error!
print('after condition')
except BreakOut:
pass
This question was asked before Python 3.4 existed but with 3.4 you can use contextlib.supress
,
suppressing your own personal exception.
See that this (runnable as is) code
from contextlib import suppress
class InterruptWithBlock(UserWarning):
"""To be used to interrupt the march of a with"""
condition = True
with suppress(InterruptWithBlock):
print('before condition')
if condition: raise InterruptWithBlock()
print('after condition')
# Will not print 'after condition` if condition is True.
So with the code in the question, you’d do:
with suppress(InterruptWithBlock) as _, open(path) as f:
print('before condition')
if <condition>: raise InterruptWithBlock()
print('after condition')
Note: If you’re (still) before 3.4, you can still make your own suppress
context manager easily.