writing multiple try and except in python - python

I want to know what is the most elegant way of writing try..except statements in python. Assume I have this code:
with open(sys.argv[1]) as f:
for line in f:
try:
do_1(line)
except:
pass
try:
do_2(line)
except:
pass
try:
do_3(line)
except:
pass
...
...
What is the best way of writing this? My actions are sequential. However, if do_1 fails I still want to perform do_2. If all of them are in one try..except block, then if do_1 fails, I will never reach do_2. Is this the right way, or can I have one except for all of d0_i actions?

It's simple enough to write this as a loop:
for action in [do_1, do_2, do_3, ...]:
try:
action(line)
except AppropriateExceptionType:
pass

I would factor out the common code which is your try/except statements. Something like:
def run_safely(f, *args):
try:
f(*args)
except SpecificException:
# handle appropriately here
pass
with open(sys.argv[1]) as f:
for line in f:
run_safely(do_1, line)
run_safely(do_2, line)
run_safely(do_3, line)

Essentially, you need each do_<Step> function to run inside the finally block of the previous one, like so:
try:
do_1(line)
except:
# Handle failure
pass
finally:
# Run regardless
try:
do_2(line)
except:
# Handle failure
finally:
# Run regardless
try:
do_3(line)
...
This chains the functions together through the finally block. Notice that in the event of an exception at any step, the exception is handled before starting the next step, which is guaranteed to run regardless of whether an exception is generated or not.
Since your functions all have the same shape (taking the same number and type of arguments), you can abstract out this pattern into a function, like tryChain below:
def tryChain(functions, *sharedArgs)
f = functions.pop()
try:
f(*sharedArgs)
finally:
tryChain(functions)
try:
tryChain([do_1, do_2, ...], line, arg2, ...)
except SpecificException:
# Handle exception here, which may be propagated from any of the actions
pass
Note that in this case, only the last exception is thrown back to the caller; the others are hidden. (You could handle the exceptions inside tryChain as well, with an except block inserted there; or, you could pass in an error handler for each step; or a map from exception types to the appropriate handler, and re-throw the error if none of them matches — but at that point, you're practically reinventing exception handling.)

Related

Functional difference in exception handling between consecutive except, nested try, tuple exception with if else code blocks

Is there a functional difference between the exception handling in code blocks 1,2,3? I want to print different messages based on the type error for example including the error code if psycopg2 error. I have read that nested try except blocks are good practice. I'm using psycopg2 as an example.
# code block 1
def my_func(fun_arg):
try:
# ... some stuff
except psycopg2.Error as error:
print(f"Error while inserting data to PostgreSQL {type(error).__name__} {error.pgcode} {error.pgerror}")
except Exception as error:
print(f'error in my_func {type(error).__name__} {error.args}')
# code block 2
def my_func(fun_arg):
try:
# ... some stuff
try:
# ... some stuff
except psycopg2.Error as error:
print(f"Error while inserting data to PostgreSQL {type(error).__name__} {error.pgcode} {error.pgerror}")
except Exception as error:
print(f'error in my_func {type(error).__name__} {error.args}')
# code block 3
def my_func(fun_arg):
try:
# ... some stuff
except (psycopg2.Error, Exception) as error:
if (type(error).__name__ in (
'DatabaseError', 'OperationalError', 'NotSupportedError',
'ProgrammingError', 'DataError','IntegrityError',))
print(f"Error while inserting data to PostgreSQL {type(error).__name__} {error.pgcode} {error.pgerror}")
else:
print(f'error in my_func {type(error).__name__} {error.args}')
In your case, there is no functional difference. In such a case, you may follow the Zen of Python to choose the one that fits best. There are a lot of ways to interpret it but I would choose block number 1 as it is the simplest, the flattest, and the most readable one.
Block number one is the preferred one for the reasons above I'd say.
Block number two is needed when all the branches of the inner try can raise the same exception and the handler of the exception is also the same. E.g.:
try:
try:
store_to_db(value)
except ValueError:
store_to_db(fallback)
except DbError:
log_error()
Block number three is just a more complicated and more difficult-to-read alternative to block number one. I cannot imagine a practical use case for it except that there is a shared code for both exceptions. E.g.:
try:
do_something()
except (DatabaseError, OperationalError, KeyError) as error:
data = collect_some_data()
if type(error).__name__ in ('DatabaseError', 'OperationalError'):
print(f"Error while inserting data to PostgreSQL {data}")
else:
print(f'error in my_func {data}')
Although isinstance is probably preferred over type(error).__name__ ==. And I would still argue that it is better to split it into multiple except clauses and repeat the call to collect_some_data but it is a matter of personal preferences.
Another thing to take into account is that it is generally preferred to "limit the try clause to the absolute minimum amount of code necessary as it avoids masking bugs" [PEP 8]. This is another reason to avoid nested try whenever possible.
I think either of the first two options are fine.
Personally I find I think the first is easier to read for a short simple function although if my_func was larger and more complex with nesting then I would opt for the second option as it makes it clear where exactly where sycopg2.Error may be raised.
The wouldn't use the third option. If you want to catch multiple exceptions use this syntax:
except (RuntimeError, TypeError, NameError):
pass

Can I have multiple try statements connecting to one except statement?

So I want to be efficient in making code in python, and I am doing something like this:
try:
#Code A
except:
#Code A
try:
#Code B
except:
#Code B
try:
#Code B
except:
#Code B
But I want to link all of them to one except block so I can use an else statement to catch em' all! (Reference intended).
So could I do something like the following?
try:
#Code A
try:
#Code B
except:
#Code C
else:
#Code D
I have tried the code, but to my self findings and limited questions that are 'similar' all I get is: Error: invalid syntax "try:". Is there anything I can do about this?
You actually don't need those extra trys at all. Whenever there's an exception raised inside the try block, all code starting there until the end of the block is skipped and the control jumps immediately to the matching except block. And as well, the else block is executed when the try block reachs its end normally. This way you don't need multiple try blocks - just concatenate them into one and you'll achieve your aim.
No you can't have multiple try block associated with only one except block. but you can have muliple except block releated to only one try block.
every try block need except block if you didn't provide one you will have an exception.
example of mulitple except block releated to one try block :-
try:
# do something
pass
except ValueError:
# handle ValueError exception
pass
except (TypeError, ZeroDivisionError):
# handle multiple exceptions
# TypeError and ZeroDivisionError
pass
except:
# handle all other exceptions
pass
suppose you perform some operation in try block and you encountered a problem then first except block will take the control if it can't handle the exception then the below except block will handle it and so on......
Note: if the first except block can handle the exception the next except block does not execute or will not come in action.
It is always good practice to have more generic except block for a specific exception type.
a single try statement with multiple except block
try:
#put your risky code
# put another risky code
pass
# multiple exception block will help to catch exact exception and can perform
# operation
except KeyError:
pass
except IOError:
pass
except ValueError:
pass
except Exception:
pass
else:
pass

How to return a value and raise an Exception

I have two objectives with this try/except statement.
It needs to return a value of 1 if no problems occurred, or 0 if any problems occurred.
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.
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.
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.
To exit a script and return an exit status, use sys.exit():
import sys
sys.exit(value_to_return)
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
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
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

Looping until a completely reliable exception

Right now I'm doing this:
try:
while True:
s = client.recv_into( buff, buff_size )
roll += buff.decode()
I repeatedly call client.recv_into until it raises an exception. Now, I know eventually, without a doubt, it will raise an exception...
Is there a cleaner way to do this? Is there some sort of loop-until-exception construct or a common way to format this?
There are two ways to do this:
Like you did
try:
while True:
s = client.recv_into( buff, buff_size )
roll += buff.decode()
except YourException:
pass
or
while True:
try:
s = client.recv_into( buff, buff_size )
roll += buff.decode()
except YourException:
break
Personally, I would prefer the second solution as the break keyword makes clear what is happening in case of the exception.
Secondly, you should only catch the exception you're awaiting (in my example YourException). Otherwise IOError, KeyboardInterrupt, SystemExit etc. will also be caught, hiding "real errors" and potentially blocking your program from exiting properly.
That appears to be the (pythonic) way to do it. Indeed, the Python itertools page gives the following recipe for iter_except (calls a function func until the desired exception occurs):
def iter_except(func, exception, first=None):
try:
if first is not None:
yield first()
while 1:
yield func()
except exception:
pass
which is almost exactly what you're done here (although you probably do want to add a except [ExceptionName] line in your try loop, as Nils mentioned).

Execute (part of) try block after except block

I know that is a weird question, and probably there is not an answer.
I'm trying to execute the rest of the try block after an exception was caught and the except block was executed.
Example:
[...]
try:
do.this()
do.that()
[...]
except:
foo.bar()
[...]
do.this() raise an exception managed by foo.bar(), then I would like to execute the code from do.that(). I know that there is not a GOTO statement, but maybe some kind of hack or workaround!
Thanks!
A try... except... block catches one exception. That's what it's for. It executes the code inside the try, and if an exception is raised, handles it in the except. You can't raise multiple exceptions inside the try.
This is deliberate: the point of the construction is that you need explicitly to handle the exceptions that occur. Returning to the end of the try violates this, because then the except statement handles more than one thing.
You should do:
try:
do.this()
except FailError:
clean.up()
try:
do.that()
except FailError:
clean.up()
so that any exception you raise is handled explicitly.
Use a finally block? Am I missing something?
[...]
try:
do.this()
except:
foo.bar()
[...]
finally:
do.that()
[...]
If you always need to execute foo.bar() why not just move it after the try/except block? Or maybe even to a finally: block.
One possibility is to write a code in such a way that you can re-execute it all when the error condition has been solved, e.g.:
while 1:
try:
complex_operation()
except X:
solve_problem()
continue
break
fcts = [do.this, do.that]
for fct in fcts:
try:
fct()
except:
foo.bar()
You need two try blocks, one for each statement in your current try block.
This doesn't scale up well, but for smaller blocks of code you could use a classic finite-state-machine:
states = [do.this, do.that]
state = 0
while state < len(states):
try:
states[state]()
except:
foo.bar()
state += 1
Here's another alternative. Handle the error condition with a callback, so that after fixing the problem you can continue. The callback would basically contain exactly the same code you would put in the except block.
As a silly example, let's say that the exception you want to handle is a missing file, and that you have a way to deal with that problem (a default file or whatever). fileRetriever is the callback that knows how to deal with the problem. Then you would write:
def myOp(fileRetriever):
f = acquireFile()
if not f:
f = fileRetriever()
# continue with your stuff...
f2 = acquireAnotherFile()
if not f2:
f2 = fileRetriever()
# more stuff...
myOp(magicalCallback)
Note: I've never seen this design used in practice, but in specific situations I guess it might be usable.

Categories

Resources