129

I have a problem with my code in the try block. To make it easy this is my code:

try:
    code a
    code b #if b fails, it should ignore, and go to c.
    code c #if c fails, go to d
    code d
except:
    pass

Is something like this possible?

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
arvidurs
  • 2,853
  • 4
  • 25
  • 36

10 Answers10

193

You'll have to make this separate try blocks:

try:
    code a
except ExplicitException:
    pass

try:
    code b
except ExplicitException:
    try:
        code c
    except ExplicitException:
        try:
            code d
        except ExplicitException:
            pass

This assumes you want to run code c only if code b failed.

If you need to run code c regardless, you need to put the try blocks one after the other:

try:
    code a
except ExplicitException:
    pass

try:
    code b
except ExplicitException:
    pass

try:
    code c
except ExplicitException:
    pass

try:
    code d
except ExplicitException:
    pass

I'm using except ExplicitException here because it is never a good practice to blindly ignore all exceptions. You'll be ignoring MemoryError, KeyboardInterrupt and SystemExit as well otherwise, which you normally do not want to ignore or intercept without some kind of re-raise or conscious reason for handling those.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 8
    Python's exception handling is just so ugly, it makes you write code that cries for C-style macros. – Elazar Jun 26 '13 at 14:19
  • 18
    @Elazar: When your code starts to look like the above, you really want to rethink what you are doing. With context managers and some refactoring, most exception-handling code can be made much more readable and maintainable. – Martijn Pieters Jun 26 '13 at 14:20
  • 3
    The question is, should I rethink it just because it is python so I must use both exceptions and indentation. Four simple operations, each should execute only if the last failed, and you get 4 levels of indentation. uh. If exceptions are good, their use should have been syntactically encouraged. – Elazar Jun 26 '13 at 14:26
  • Refactoring is nice, but it comes with other issues like binding and scoping. – Elazar Jun 26 '13 at 14:27
  • 4
    There is just not enough detail in the OP to go into possible alternatives. – Martijn Pieters Jun 26 '13 at 14:28
  • 1
    @MartijnPieters: still, some links to relevant context manager usage that might help in a case like this might be nice. – naught101 Feb 02 '21 at 04:15
  • 2
    @naught101: it's way, way too broad to give anything as focused as links to relevant context manager usage. The best I can think of is [talks by Raymond Hettinger that demonstrate code refactorings that use context managers](https://www.youtube.com/watch?v=wf-BqAjZb8M). – Martijn Pieters Feb 02 '21 at 12:04
  • @MartijnPietersthat's great, thanks. Relevant part around 20 minutes in. – naught101 Feb 03 '21 at 22:47
52

You can use fuckit module.
Wrap your code in a function with @fuckit decorator:

@fuckit
def func():
    code a
    code b #if b fails, it should ignore, and go to c.
    code c #if c fails, go to d
    code d
Mostafa Bahri
  • 2,303
  • 2
  • 19
  • 26
19

Extract (refactor) your statements. And use the magic of and and or to decide when to short-circuit.

def a():
    try: # a code
    except: pass # or raise
    else: return True

def b():
    try: # b code
    except: pass # or raise
    else: return True

def c():
    try: # c code
    except: pass # or raise
    else: return True

def d():
    try: # d code
    except: pass # or raise
    else: return True

def main():   
    try:
        a() and b() or c() or d()
    except:
        pass
Inbar Rose
  • 41,843
  • 24
  • 85
  • 131
  • 5
    I think a decorator would fit here. – Elazar Jun 26 '13 at 14:05
  • If `b` fails (raises an exception), `c` will **not** be executed, nor will `d`. – Martijn Pieters Jun 26 '13 at 14:05
  • it is **Commented** it is just there as a comment.... could write `pass` too..... edited it, better? – Inbar Rose Jun 26 '13 at 14:06
  • I've asked for clarification from the OP as what he wants is ambiguous, but your code runs `c` even if `b` succeeds. – Martijn Pieters Jun 26 '13 at 14:08
  • 1
    Okay that's what I thought, for each code I have to create a new try block. Because let's say I have several codes to be run, it should continue even if an exception occurs. Because what it does now, when the first exception occurs, when B fails, it will skip the other codes.Which is not what I want. Even if B fails it should try C, if C fails it should try D. No matter if error or not it should run through all lines. Hope it's better to understand now. – arvidurs Jun 26 '13 at 14:39
  • 1
    `except: pass ... else: return True` is an obscure way of implicitly saying `except: return None ... else: return True`. Better to be explicit. – smci Dec 14 '17 at 20:59
10

If you don't want to chain (a huge number of) try-except clauses, you may try your codes in a loop and break upon 1st success.

Example with codes which can be put into functions:

for code in (
    lambda: a / b,
    lambda: a / (b + 1),
    lambda: a / (b + 2),
    ):
    try: print(code())
    except Exception as ev: continue
    break
else:
    print("it failed: %s" % ev)

Example with arbitrary codes (statements) directly in the current scope:

for i in 2, 1, 0:
    try:
        if   i == 2: print(a / b)
        elif i == 1: print(a / (b + 1))
        elif i == 0: print(a / (b + 2))
        break        
    except Exception as ev:
        if i:
            continue
        print("it failed: %s" % ev)
kxr
  • 4,841
  • 1
  • 49
  • 32
5

You could try a for loop


for func,args,kwargs in zip([a,b,c,d], 
                            [args_a,args_b,args_c,args_d],
                            [kw_a,kw_b,kw_c,kw_d]):
    try:
       func(*args, **kwargs)
       break
    except:
       pass

This way you can loop as many functions as you want without making the code look ugly

Yesh
  • 976
  • 12
  • 15
4

Lets say each code is a function and its already written then the following can be used to iter through your coding list and exit the for-loop when a function is executed without error using the "break".

def a(): code a
def b(): code b
def c(): code c
def d(): code d

for func in [a, b, c, d]:  # change list order to change execution order.
   try:
       func()
       break
   except Exception as err:
       print (err)
       continue

I used "Exception " here so you can see any error printed. Turn-off the print if you know what to expect and you're not caring (e.g. in case the code returns two or three list items (i,j = msg.split('.')).

ZF007
  • 3,708
  • 8
  • 29
  • 48
  • 2
    having thiem as a list of functions (that are already called) will immediately execute them in the list itself, before even reaching `try` – Yesh Aug 10 '20 at 18:06
2

I ran into this problem, but then it was doing the things in a loop which turned it into a simple case of issueing the continue command if successful. I think one could reuse that technique if not in a loop, at least in some cases:

while True:
    try:
        code_a
        break
    except:
        pass

    try:
        code_b
        break
    except:
        pass

    etc

    raise NothingSuccessfulError
skyking
  • 13,817
  • 1
  • 35
  • 57
1

I use a different way, with a new variable:

continue_execution = True
try:
    command1
    continue_execution = False
except:
    pass
if continue_execution:
    try:
        command2
    except:
        command3

to add more commands you just have to add more expressions like this:

try:
    commandn
    continue_execution = False
except:
    pass
1

Building on kxr's answer (not enough rep to comment) you can use For/Else (see docs) to avoid checking the i value. The else clause only executes when the for finishes normally, so it gets skipped when the break executes

for i in 2, 1, 0:
    try:
        if   i == 2: print(a / b)
        elif i == 1: print(a / (b + 1))
        elif i == 0: print(a / (b + 2))
        break        
    except Exception as ev:
        continue
else:
    print("it failed: %s" % ev)
Jaxom3
  • 31
  • 3
0

Like Elazar suggested: "I think a decorator would fit here."

# decorator
def test(func):
    def inner(*args, **kwargs):
        try:
            func(*args, **kwargs)
        except: pass
    return inner

# code blocks as functions
@test
def code_a(x):
    print(1/x)

@test
def code_b(x):
    print(1/x)

@test
def code_c(x):
    print(1/x)

@test
def code_d(x):
    print(1/x)

# call functions
code_a(0)
code_b(1)
code_c(0)
code_c(4)

output:

1.0
0.25
LuettgeM
  • 133
  • 2
  • 4