14

I have two objectives with this try/except statement.

  1. It needs to return a value of 1 if no problems occurred, or 0 if any problems occurred.
  2. It needs to raise an exception and end the script.

I have the return value working. I also have the SystemExit() working. But together, they aren't working.

My Python Script (that's relevant):

except IOError:
    value_to_return = 0
    return value_to_return
    raise SystemExit("FOOBAR")

With this, it ignores the raise SystemExit("FOOBAR") line completely. How do I go about getting a returned value and still raise SystemExit("FOOBAR")? This may be elementary to some, but I'm actually having quite a bit of difficulty with it.

David Cain
  • 16,484
  • 14
  • 65
  • 75
misterbear
  • 803
  • 2
  • 13
  • 33
  • If you raise SystemExit and actually want your script to end, where should the return value go (The script ended after all)? or do you want to first catch the exception, return, do some error handling, and exit later? –  Jan 21 '14 at 00:26
  • if you return, the raise is not going to happen. You can not do both anyway! Choose what do you want, exit or return. – Raul Guiu Jan 21 '14 at 00:27
  • 1
    Just guessing, but you probably need to return 0 and have the *caller* raise `SystemExit`. There's no way to do both in the same function. – roippi Jan 21 '14 at 00:29
  • Are you trying to return a status code to the shell? – Daniel Jan 21 '14 at 00:35
  • I double checked with the lead engineer. What he wants is for my script to return a 0 since something is wrong, and that the script should end because it's rendered useless from that point on. So I do need to return a 0 and end the script. I tried all the other answers below, none worked. – misterbear Jan 21 '14 at 01:47
  • 1
    Still not making any sense there. 0 is the exit code for "things went fine", so it doesn't sound like a sensible exit code in this case. If it is an exit code, `raise SystemExit` with no return should be fine, or `sys.exit()` if you've imported `sys`, or just have control flow run off the end of the script. – user2357112 Jan 21 '14 at 01:59

6 Answers6

15

Returning and raising are mutually exclusive.

Raising SystemExit will end the script. A few cleanup routines get to run, and if the caller really, really wants to, they can catch the SystemExit and cancel it, but mostly, you can think of it as stopping execution right there. The caller will never get a chance to see a return value or do anything meaningful with it.

Returning means you want the script to continue. Continuing might mean having the caller raise SystemExit, or it might mean ignoring the error, or it might mean something else. Whatever it means is up to you, as you're the one writing the code.

Finally, are you sure you should be handling this error at all? Catching an exception only to turn it into a system shutdown may not be the most useful behavior. It's not a user-friendly way to deal with problems, and it hides all the useful debugging information you'd get from a stack trace.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • @ingyhere: No, that tutorial section just sucks. IIRC someone copied a poorly-written description from a Github comment without examining it closely enough. `finally` will suspend the exception's propagation, and then the `return` will cancel the exception and return `"value"` instead. – user2357112 Jan 20 '20 at 22:58
  • 1
    The [language reference](https://docs.python.org/3.7/reference/compound_stmts.html#the-try-statement) has a more accurate description, particularly "If the finally clause executes a return or break statement, the saved exception is discarded". The language reference tends to be better than the tutorial (but the language reference isn't perfect either; for example, it often fails to mention how various operations can be customized by magic methods). – user2357112 Jan 20 '20 at 23:04
  • OK, @user2357112supportsMonica. I ran a sample program, and you're indeed correct. The [Python.org link I presented above](https://docs.python.org/3.7/tutorial/errors.html#defining-clean-up-actions) unfortunately states verbatim, "If the exception is not handled by an except clause, the exception is re-raised after the finally clause has been executed." It also discusses an example where finally is executed and then the exception. That's not necessarily contradictory to what you wrote, though. In other SO posts there is contradiction, unfortunately. Earlier comment deleted for clarity. – ingyhere Jan 21 '20 at 06:41
5

You can raise an error with a 'returning_value' argument to be used after the calling.

Another pythonic answer to your problem could be to make use of the error arguments in the raise and then, in your call manage the error to get the value, convert it from string and get your 'return-ish'.

def your_f():
    try:
      some_io_thingy_ok()
      return 1
    except IOError:
        raise SystemExit("FOOBAR", 0)

try:
    my_returning_value = your_f()
except SystemExit as err:
    my_returning_value = err.args[1]


print(my_returning_value)

From Python 3 docs :

When an exception occurs, it may have an associated value, also known as the exception’s argument. The presence and type of the argument depend on the exception type.

The except clause may specify a variable after the exception name. The variable is bound to an exception instance with the arguments stored in instance.args. For convenience, the exception instance defines str() so the arguments can be printed directly without having to reference .args. One may also instantiate an exception first before raising it and add any attributes to it as desired.

Natacha
  • 1,132
  • 16
  • 23
2

To exit a script and return an exit status, use sys.exit():

import sys
sys.exit(value_to_return)
Dan Getz
  • 8,774
  • 6
  • 30
  • 64
  • Couldn't you also use `__builtins__.exit(value_to_return)` as well? – UCYT5040 May 31 '21 at 21:52
  • @UltimateCreeper yes, it seems so. `sys.exit` is apparently preferred in programs, see https://stackoverflow.com/questions/6501121/difference-between-exit-and-sys-exit-in-python – Dan Getz Jun 01 '21 at 00:38
0

You can't "raise" and "return" in the same time, so you have to add a special variable to the return value (e.g: in tuple) in case of error.

E.g: I have a function (named "func") which counts something and I need the (partial) result even if an exception happened during the counting. In my example I will use KeyboardInterrupt exception (the user pressed CTRL-C).

Without exception handling in the function (it's wrong, in case of any exception the function doesn't give back anything):

def func():
    s=0
    for i in range(10):
        s=s+1
        time.sleep(0.1)
    return s


x=0
try:
    for i in range(10):
       s=func()
       x=x+s
except KeyboardInterrupt:
    print(x)
else:
    print(x)

And now I introduce a boolean return value (in a tuple, next to the original return value) to indicate if an exception happened. Because in the function I handle only the KeyboardInterrupt exception, I can be sure that's happened, so I can raise the same where I called the function:

def func():
    try:
        s=0
        for i in range(10):
            s=s+1
            time.sleep(0.1)
    except KeyboardInterrupt: # <- the trick is here
        return s, True        # <- the trick is here
    return s, False           # <- the trick is here


x=0
try:
    for i in range(10):
        s,e=func()
        x=x+s
        if e:                         # <- and here
            raise KeyboardInterrupt   # <- and here
except KeyboardInterrupt:
    print(x)
else:
    print(x)

Note: my example is python3. The time module is used (in both code above), but I haven't import it just to make it shorter. If you want to really try it, put at the beginning:

import time
redseven
  • 849
  • 6
  • 11
0

i was looking for an answer without using try, use 'finally' keyword like this.. if any one knows fill me in here is an answer for your poblem

try:
    9/0#sample error "don't know how to make IOError"
except ZeroDivisionError:
    value_to_return = 0
    raise SystemExit("FOOBAR")
finally:return value_to_return
-1

I think what you may be looking for is something more like this:

def some_function():
    # this function should probably do some stuff, then return 1 if
    # it was successful or 0 otherwise.
    pass

def calling_function():
    a = some_function()
    if a == 1:
        raise SystemExit('Get the heck outta here!')
    else:
        # Everything worked!
        pass
Cody Piersall
  • 8,312
  • 2
  • 43
  • 57