Python alternative assignment when exceptions occur using context manager - python

In Python, I can assign alternative values to a variable if the first assignment to this variable raise exceptions, something like:
try:
a = 1/0
except Exception:
a = 0
I wonder can we replace the try/except with context manager?
Here what I tried:
from contextlib import contextmanager
#contextmanager
def tryo(*exception_list):
t = tuple(i for i in exception_list[0]
if isinstance(i,Exception) or (Exception,))
try:
yield
except t as e:
print e
with tryo([(Exception, 0)]):
a = 1/0
I guess I must do something instead of yield but don't know what must I do. Any suggestion?

The exception (ZeroDivisionError in this case) is not caused by assignment failure, but becasue of dividing by 0.
The first code can be converted as follow:
a = 0
try:
a = 1 / 0
except Exception: # ZeroDivisionError:
pass
How about the following approach (yielding default value, change the value in with statement body)?
>>> from contextlib import contextmanager
>>>
>>> #contextmanager
... def tryo(exceptions, default):
... try:
... yield default
... except exceptions:
... pass
...
>>> with tryo((Exception), 0) as a: # ZeroDivisionError:
... a = 1 / 0
...
>>> with tryo((Exception), 0) as b: # ZeroDivisionError:
... b = 4 / 2
...
>>> a
0
>>> b
2.0

There is no way for the context manager to know what you are doing within the context. It especially won’t be able to tell which variable you are assigning a value to; it also won’t be able to access that variable; and even if it could, there would be no guarantee that you only did that one assigment inside the context manager too. So, no, you can’t do it like that.
What you could do however, is to do it the other way around. Your 0 is a default value, so you would set that one first. Afterwards, you try to assign your actual value 1/0 and ignore the ZeroDivisionError. So it would look like this:
a = 0
try:
a = 1/0
except ZeroDivisionError:
pass
And that you can do with a context manager, with contextlib.suppress:
a = 0
with suppress(ZeroDivisionError):
a = 1/0

you can use Decorator like:
def my_decorator(exception_list):
def real_decorator(func):
def fn_wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except tuple(e for e, _ in exception_list) as e:
for error, default in exception_list:
if isinstance(e, error):
return default
else:
# this Exception not in exception_list
raise e
return fn_wrapper
return real_decorator
#my_decorator([(ZeroDivisionError, 1),
(IndexError, 2),
(ValueError, 3),
(Exception, 0)],
)
def div_working():
import random
e = random.choice((ZeroDivisionError, IndexError, ValueError, Exception, 100, 200, 300))
if isinstance(e, int):
return e
else:
print e
raise e
for _ in range(10):
a = div_working()
print a
print "= " * 10

Related

How to limit the number of failed attempts to call a function?

How rewrite this code in a pythonic way?
tried = 0
while tried < 3:
try:
function()
break
except Exception as e:
print e
tried += 1
Is there a built-in function I could use?
A more pythonic way to do something N times is to use xrange with the _ variable:
for _ in xrange(3):
try:
function()
break
except Exception as e:
print e
Also, consider catching a more specific exception instead of the root Exception class.
You can use a retry decorator:
#retries(3)
def execTask():
f()
One simpler than the one in the provided link could look like this:
def retry(times=3):
def do_retry(f, *args, **kwargs):
cnt = 0
while cnt < times:
try:
f(*args, **kwargs)
return
except:
cnt += 1
return do_retry
And could be used like this:
#retry(3)
def test():
print("Calling function")
raise Exception("Some exception")
tried = 0
while tried < 3:
try:
function()
break
except Exception as e:
print e
tried += 1
It's almost exactly how you wrote it, except you need a colon at the end of your while line and move the break to the "try" block.
Motivation
Given the motivation of the OP was to limit the number of failing attempt to run a function(), the following code does not provide any artificial gymnastics, but both limits the number of tries and retains the actual number of attempts for ex-post analyses ( if needed down the road )
tried = 0
while tried < 3:
try:
function() # may throw an Exception
break # on success pass through/return from function()
except Exception as e:
print e
tried += 1
# retains a value of tried for ex-post review(s) if needed

Is this function correct?

Is it correct to call that function in some exceptions ?
Is that process correct ? Is it better to process each exceptions ?
def close_all():
try:
ftp.close()
except:
pass
try:
tar.close()
except:
pass
try:
savelist.close()
except:
pass
try:
os.remove(tarname)
except:
pass
exit()
Thanks in advance.
I think you should handle each exception one by one. This will shorten your code. First of all note all the exceptions that ftp.close() and other methods will raise. Then handle them one by one.
Example:
>>> a = 5 # a is an integer
>>> b = "Bingo" # b is a string
>>>
>>> def add_five():
try:
c + 5 # c is not defined. NameError exception is raised
except NameError:
b + 5 # b is a string. TypeError exception is raised
except TypeError:
a + 5 # a is int. No exception is raised
except:
# This last except clause is just in case you forgot to handle any exception
pass
>>>

Multiple try codes in one block

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?
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.
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
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
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)
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('.')).
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
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
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
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

How to write multiple try statements in one block in python?

I want to do:
try:
do()
except:
do2()
except:
do3()
except:
do4()
If do() fails, execute do2(), if do2() fails too, exceute do3() and so on.
best Regards
If you really don't care about the exceptions, you could loop over cases until you succeed:
for fn in (do, do2, do3, do4):
try:
fn()
break
except:
continue
This at least avoids having to indent once for every case. If the different functions need different arguments you can use functools.partial to 'prime' them before the loop.
I'd write a quick wrapper function first() for this.
usage: value = first([f1, f2, f3, ..., fn], default='All failed')
#!/usr/bin/env
def first(flist, default=None):
""" Try each function in `flist` until one does not throw an exception, and
return the return value of that function. If all functions throw exceptions,
return `default`
Args:
flist - list of functions to try
default - value to return if all functions fail
Returns:
return value of first function that does not throw exception, or
`default` if all throw exceptions.
TODO: Also accept a list of (f, (exceptions)) tuples, where f is the
function as above and (exceptions) is a tuple of exceptions that f should
expect. This allows you to still re-raise unexpected exceptions.
"""
for f in flist:
try:
return f()
except:
continue
else:
return default
# Testing.
def f():
raise TypeError
def g():
raise IndexError
def h():
return 1
# We skip two exception-throwing functions and return value of the last.
assert first([f, g, h]) == 1
assert first([f, g, f], default='monty') == 'monty'
It seems like a really odd thing to want to do, but I would probably loop over the functions and break out when there were no exception raised:
for func in [do, do2, do3]:
try:
func()
except Exception:
pass
else:
break
Here is the simplest way I found, just embed the try under the previous except.
try:
do()
except:
try:
do2()
except:
do3()
You should specify the type of the exception you are trying to catch each time.
try:
do()
except TypeError: #for example first one - TypeError
do_2()
except KeyError: #for example second one - KeyError
do_3()
and so on.
if you want multiple try statments you can do it like this, including the except statement. 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
import sys
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
raise

Can I get the local variables of a Python function from which an exception was thrown?

I'm writing a custom logging system for a project. If a function throws an exception, I want to log its local variables. Is it possible to access the raising function's local variables from the except block that caught the exception? For example:
def myfunction():
v1 = get_a_value()
raise Exception()
try:
myfunction()
except:
# can I access v1 from here?
It's generally cleaner design to pass the value to the exception, if you know that your exception handling code is going to need it. However, if you're writing a debugger or something like that, where you will need to access variables without knowing which ones they are in advance, you can access an arbitrary variable in the context where the exception was thrown:
def myfunction():
v1 = get_a_value()
raise Exception()
try:
myfunction()
except:
# can I access v1 from here?
v1 = inspect.trace()[-1][0].f_locals['v1']
The functionality of the trace function, and the format of the traceback objects it deals with, are described in the inspect module documentation.
You can look up the local variables in the frame object, which you can get from sys.exc_info.
>>> import sys
>>> def f(a):
... b = a - 1
... print 1.0 / b
...
>>> try:
... f(1)
... except Exception, e:
... print sys.exc_info()[2].tb_next.tb_frame.f_locals
...
{'a': 1, 'b': 0}
You'll have to include the appropriate number of tb_nexts depending on from how deep in the stack the exception was thrown.
def myFunction()
v1 = get_a_value()
raise Exception(v1)
try:
myFunction()
except Exception, e:
v1 = e.args[0]
Yes, if it's your own exception type. Like this:
>>> class MyExn(Exception):
... def __init__(self, val):
... self.val = val
... def __str__(self):
... print "something wrong with:", str(self.val)
...
>>> def foo():
... val = 42
... raise MyExn(val)
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in foo
__main__.MyExnsomething wrong with: 42
>>> # Or in a try block:
>>> try:
... foo()
... except MyExn as e:
... print e.val
...
42
>>>
try:
myfunction()
except:
import sys
type, value, tb = sys.exc_info()
while tb.tb_next:
tb = tb.tb_next
frame = tb.tb_frame
print frame.f_locals['v1']
Useful for "handling" exceptions without using raise Exception(some_def_var) and also without using a library such as "inspect", for example.
However, I think if is to handle the exception "in real", might be better to use something like raise Exception(some_def_var). See #capfredf answer.
class MyClass():
def __init__(self):
self.my_attr_I = ""
self.my_attr_II = ""
def my_def_I(self):
try:
self.my_def_II()
except Exception as e:
print(self.my_attr_I)
print(self.my_attr_II)
def my_def_II(self):
self.my_attr_I = "TO EXCEPT I"
self.my_attr_II = "TO except II"
# NOTE: We will treat it as a general exception! By Questor
zero_division = 1/0

Categories

Resources