Handling exception in main function interface - python

I have a main function that takes a list argument and proceeds with every element in the list. It is not necessary for me to stop the program whenever one element raises an exception. The problem is that the input list is passed through different functions and it is a pain to handle exceptions for all those functions.
Is there any way to continue to proceed with the next element when the current element in the list raises an exception?
Let's say func3 raise an exception at list_[0], then main will continue with list_[1] from func1, and then func2,... –
Some pseudo-code:
def main(list_) -> None:
func1(list_)
func2(list_)
func3(list_)
Expected behavior:
def main(list_) -> None:
try:
func1(list_)
func2(list_)
func3(list_)
except:
continue with the next element
print(Element i raise Exception..., Skipping i...)

Each function needs to be in its own try block.
def main(list_) -> None:
for f in (func1, func2, func3):
try:
f(list_)
except Exception as e:
print(e)

With a “reversed” list of the functions, this while-loop might be suitable for the body of the main function:
funcs = [funcN, funcN-1, …, func2, func1]
while any(funcs):
try: funcs.pop()(list_)
except: continue

If all your functions has the same input list_, the other answers should be enough and are preferable due to simplicity. But in case the arguments vary, you might want to wrap the call to those functions inside a decorator in where you would catch the errors:
Style 1
def catch_exc(func, *args, **kwargs):
try:
func(*args, **kwargs)
except Exception as error:
print(func, "Element i raise Exception..., Skipping i...", error)
else:
print(func, "Success")
def func1(list_): raise Exception("Anything")
def func2(list_): raise Exception("Something")
def func3(list_): return "Success"
def func4(list_): raise Exception("Whatever")
def main(list_) -> None:
catch_exc(func1, list_)
catch_exc(func2, list_)
catch_exc(func3, list_)
catch_exc(func4, list_)
main([1,2,3,4])
Style 2
def catch_exc(func):
def wrapper(*args, **kwargs): # Ideally decorate this with <functools.wraps(func)>
try:
func(*args, **kwargs)
except Exception as error:
print(func, "Element i raise Exception..., Skipping i...", error)
else:
print(func, "Success")
return wrapper
#catch_exc
def func1(list_): raise Exception("Anything")
#catch_exc
def func2(list_): raise Exception("Something")
#catch_exc
def func3(list_): return "Success"
#catch_exc
def func4(list_): raise Exception("Whatever")
def main(list_) -> None:
func1(list_)
func2(list_)
func3(list_)
func4(list_)
main([1,2,3,4])
Output
<function func1 at 0x149f3d911c10> Element i raise Exception..., Skipping i... Anything
<function func2 at 0x149f3d911ee0> Element i raise Exception..., Skipping i... Something
<function func3 at 0x149f3d911f70> Success
<function func4 at 0x149f3d91b040> Element i raise Exception..., Skipping i... Whatever

Related

How to decorate iterables with an error handler?

Suppose we have two kinds of methods: one returns a list, the other returns an iterator. So they are very comparable in the sense that both return values are iterable.
I'd like to write a decorator that catches errors inside the iteration. The problem is that the iterator is returned without iteration and so no errors will be caught.
In the below code, the wrapped_properly decorator works around the issue by providing two separate wrappers, a default one (wrapper) and one specifically for generator functions (generatorfunctionwrapper). The approach feels quite complicated and verbose.
from inspect import isgeneratorfunction
from functools import wraps
def failing_generator():
for i in range(1, 5):
if i % 2 == 0:
print('I dont like even numbers.')
raise ValueError(i)
yield i
def wrapped_badly(fn):
#wraps(fn)
def wrapper(*args, **kwargs):
try:
return fn(*args, **kwargs)
except ValueError as err:
print('Not to worry.')
return wrapper
def wrapped_properly(fn):
#wraps(fn)
def wrapper(*args, **kwargs):
try:
return fn(*args, **kwargs)
except ValueError as err:
print('Not to worry.')
#wraps(fn)
def generatorfunctionwrapper(*args, **kwargs):
try:
yield from fn(*args, **kwargs)
except ValueError as err:
print('Not to worry.')
if isgeneratorfunction(fn):
return generatorfunctionwrapper
else:
return wrapper
for x in wrapped_properly(failing_generator)():
print(x)
# Prints:
# 1
# I dont like even numbers.
# Not to worry.
for x in wrapped_badly(failing_generator)():
print(x)
# Prints:
# 1
# I dont like even numbers.
# Traceback (most recent call last):
# ...
# ValueError: 2
Is there a better/more pythonic way to do this?
I would suggest returning an iterator no matter what iterable the original function returns.
def wrapped(fn):
def wrapper(*args, **kwargs):
try:
yield from iter(fn(*args, **kwargs))
except ValueError as err:
print('Not to worry')
return wrapper

How to stop execution of outer function from a inner function?

Here is what I want to do:
def bfunc():
try:
do_someting
except Exception as e:
return
def afunc():
bfunc()
xxx
def cfunc():
xxx
def main():
afunc()
cfunc()
in bfunc(),I catch the exception.Now in my main(), I want to stop the afunc() execution when an exception occurs but proceed to execute cfunc().
How can I do this or is there any other way to catch the exception without too many nested try statements?
Tx
Because bfunc() is a function, therefore, to stop the execution of bfunc you can simply use return to stop bfunc. This won't affect cfunc because return only affect bfunc.
def bfunc():
try:
# do_someting
except Exception as e:
return # Exit the bfunc() immediately
You can use below code to see whether print will work or not
def bfunc():
try:
raise IndexError
except Exception as e:
return
def main():
bfunc()
print("Hello world")
if __name__ == "__main__":
main()
Just move the try exception block to afunc. It should give the effect you want.
def bfunc():
do_someting
def afunc():
try:
bfunc()
except Exception as e:
return
xxx #you can move it to try block in order to catch exceptions here too, but I don't know if it's what you like to do

Retry in python methods

This is the standard language-neutral approach
import logging
logger = logging.getLogger(__file__)
class SomeClass(object):
max_retry = 2
def get(self, key):
try:
return self.__get_value(key)
except Exception:
logger.error("Exception occured even after retrying...")
raise
def __get_value(self, key, retry_num=0):
try:
return self.connection().get(key)
except Exception:
logger.error("Exception occured")
if retry_num < self.max_retry:
retry_num += 1
logger.warning("Retrying!!! Retry count - %s", retry_num)
self.__get_value(key, retry_num)
else:
raise
Is there any better pythonic way to do this?
Cleaner approach would be not to change state of the class since it's retry just for the function call (current implementation wouldn't work as expected when the method is called multiple times). I'd prefer a retry decorator (as loop with break when succeed) used as:
...
#retry(n_times=2, catch=Exception)
def get (self, key):
...

How to return an object multiple times in Python?

I have a problem when doing an exception handling in a Python class.
My class structure is like:
class base():
def func():
try:
# some codes to deal with requests headers in here
requests.get('...', timeout=0.1)
return something
except:
# So when timeout in request occurs, func() will return 'Error'
return 'Error'
def A():
func()
def B():
func()
# there are about 10 functions that have called func().
def index():
reply = A()
reply = B()
# and A() B() functions are called here.
return reply
My question is, is there a way to return an 'Error' to index function directly, instead of doing exception handling every time when calling it? That is, change func() only, and it has to return 2 times(func() -> A() -> index()), so reply in index function will be 'Error'.
def test(a = 1):
try:
if a:
raise Exception
else:
return a+10
except:
return "error"
You can try something like this:
def func():
try:
# the area may raise excetion
pass
except Exception1:
# anything you like
return 'error'
except Exception2:
# anything you like
return 'error'
Using requests.Timeout
def func():
try:
# some codes to deal with requests headers in here
rq = requests.get('...', timeout=0.1)
return 'something'
except requests.Timeout as err:
# So when timeout in request occurs, func() will return 'Error'
return ('Error {}'.format(err))

Timeout a function inside a class with a decorator

I'm trying to put a timeout on a function send.
I have found some elements in these posts :
https://stackoverflow.com/a/494273/3824723
https://stackoverflow.com/a/2282656/3824723
https://stackoverflow.com/a/11731208/3824723
The first one seems to apply to every function and not to a precise one, this is why I chose a decorator implementation like the second one.
I tried to mix it up and I have this :
from functools import wraps
import os
import signal
class TimeoutError(Exception):
pass
def timeout_func(error_message="Timeout in send pipe!"):
def decorator(func):
def _handle_timeout(signum, frame):
if args[0].action=="warn":
print "WARNING : ",error_message
elif args[0].action=="kill":
raise TimeoutError(error_message)
def wrapper(*args, **kwargs):
print args
signal.signal(signal.SIGALRM, _handle_timeout,args[0].action)
signal.alarm(args[0].seconds)
print str(args)
try:
result = func(*args, **kwargs)
finally:
signal.alarm(0)
return result
return wraps(func)(wrapper)
return decorator
class Link(object):
def __init__(self,timeout=1,action="warn"):
self.timeout=timeout
self.action=action
#timeout_func
def send(self,value):
print "working : ", value
It gives me this :
In [6]: l=Link()
In [7]: l.send(1)
--------------------------------------------------------------------------- TypeError Traceback (most recent call
last) in ()
----> 1 l.send(1)
TypeError: decorator() takes exactly 1 argument (2 given)
My issue is that I would like to pass the timeout value second to the decorator through the Link's self. I don't fully understand the whole decorator mechanism here, and can't figure out what is wrong.
Can someone explain me how this decorator works, and what should I modify to fix it? Or if you think of a simpler/more explicit solution to implement it ?
So I've been debugging my issue, and I found a working solution:
from functools import wraps
import signal
class TimeoutError(Exception):
pass
def timeout_func(f):
def _handle_timeout(signum, frame):
raise TimeoutError("timeout error")
def wrapper(*args):
signal.signal(signal.SIGALRM, _handle_timeout)
signal.setitimer(signal.ITIMER_REAL,args[0].timeout) #args[0] is self of Link class here
try:
result = f(*args,**kwargs)
finally:
signal.alarm(0)
return result
return wrapper
class Link(object):
def __init__(self,timeout=0.1,action="warn"):
self.timeout=timeout
self.action=action
def send(self,value): # I use this func to handle the exceptions
try:
self.send_timeout(value) # call the timeout function
except TimeoutError as e: # handle Timeout exception
if self.action=="warn":
print "WARNING : Timeout error in pipe send!"
elif self.action=="kill":
print "Killing Link : ", e
raise
except (Exception,KeyboardInterrupt) as e:
print "Exception in link : ", e
raise
#timeout_func # apply timeout decorator to this function
def send_timeout(self,value):
# DO STUFF HERE
To call it :
l=Link()
l.send("any value")
I use signal.setitimer(signal.ITIMER_REAL,args[0].timeout) because it allows setting a timeout < 1 second, wich was not the case with signal.signal(), wich only accept integer as timer.

Categories