a cleaner way to approach try except in python - python

So, let say I have 3 different calls called something, something1 and something2.
and right now, im calling it like
try:
something
something1
something2
except Keyerror as e:
print e
Note that in the above code, if something fails, something1 and something2 will not get executed and so on.
The wanted outcome is
try:
something
except KeyError as e:
print e
try:
something1
except KeyError as e:
print e
try:
something2
except KeyError as e:
print e
How can I achieve the above code without so many try except blocks.
EDIT:
So, the answer I chose as correct worked. But some of the others worked as well. I chose that because it was the simplist and I modified it a little.
Here is my solution based on the answer.
runs = [something, something1, something2]
for func in runs:
try:
func()
except Keyerror as e:
print e

You could try this, assuming you wrap things in functions:
for func in (something, something1, something2):
try:
func()
except Keyerror as e:
print e

Here's a little context manager I've used for similar situations:
from contextlib import contextmanager
#contextmanager
def ignoring(*exceptions):
try:
yield
except exceptions or Exception as e:
print e
with ignoring(KeyError):
something()
# you can also put it on the same line if it's just one statement
with ignoring(KeyError): something1()
with ignoring(KeyError): something2()
A Python 3 version could let you parameterize what to do when an exception occurs (the keyword-only arguments are needed here):
from contextlib import contextmanager
#contextmanager
def ignoring(*exceptions, action=print):
try:
yield
except exceptions or Exception as e:
callable(action) and action(e)
Then you could pass in some function other than print (such as a logger, assumed to be a function named log) or if you don't want anything, pass in None (since it checks to see if the action is callable):
with ignoring(KeyError, action=log): something()

I would go with something like this:
def safe_do(*statements):
for statement, args, kwargs in statements:
try:
statement(*args, **kwargs)
except KeyError as e:
print e
# usage:
safe_do(
(something1, [], {}),
(something2, [], {}),
)
But if you are expecting only one element to be missing in statements than why don't you if it?
if some_key1 in some_dict1:
something1
if some_key2 in some_dict2:
something2
much more readable and without any magic

Other possibility
def mydec(func):
def dec():
try:
func()
except KeyError as e:
print(e)
return dec
#mydec
def f1():
print('a')
#mydec
def f2():
print('b')
raise KeyError('Test')
f1()
f2()

This greatly depends on whether or not you're doing similar tasks, or very different tasks. For example, if your something lines are all very similar you could do the following:
def something(my_dict):
try:
d = my_dict['important_key'] # Just an example, since we
return d # don't know what you're really doing
except KeyError as e:
print e
something(dict1)
something(dict2)
something(dict3)
However, if your tasks are wildly different, this approach may not be applicable. To a certain degree you're asking "How do I write efficient code", and the answer to that depends on what code you're writing.

In python3, if you want to input a function with its args and kwargs, you can use the code below:
def safe_do(**statement):
try:
statement['func'](*statement['args'],**statement['kwargs'])
except Exception as e:
print(e)
print(statement['func'])
print(statement['args'])
print(statement['kwargs'])
def divide(a,b):
print(a/b)
safe_do(func=divide,args=[1,0],kwargs={})
In my colab notebook, I presented it.

Related

Dynamically build except statements in python

So I have multiple functions that perform different actions. Error handling is pretty much similar among functions, with slight variations though.
ErrorA and ErrorB are being handled in all functions. I would like to refactor this to avoid repeating the except clauses for ErrorA and B in every place. Is there a way in Python to get this? I do not want to change code behavior nor define nested try-except blocks. Your answers are very welcome!
def func_a():
try:
do_action_a()
except ErrorA:
handle_error_a()
except ErrorB:
handle_error_b()
except ErrorC:
handle_error_c()
def func_b():
try:
do_action_b()
except ErrorA:
handle_error_a()
except ErrorB:
handle_error_b()
except ErrorD:
handle_error_d()
def func_c():
try:
do_action_c()
except ErrorA:
handle_error_a()
except ErrorB:
handle_error_b()
except Exception:
handle_general_exception()
So, the most straightforwar way would be to refactor the handling of ErrorA and ErrorB into it's own function, something like:
def execute_with_a_b_handling(func, *args, **kwargs):
try:
return func(*args, **kwargs)
except ErrorA:
handle_error_a()
except ErrorB:
handle_error_b()
def func_a():
try:
execute_with_a_b_handling(do_action_a)
except ErrorC:
handle_error_c()
def func_b():
try:
execute_with_a_b_handling(do_action_b)
except ErrorD:
handle_error_d()
def func_c():
try:
execute_with_a_b_handling(do_action_c)
except Exception:
handle_general_exception()
Of course, with a better name.
Personally, I quite like using context managers in this situation. These are best used where there is a point in the code from which it is worth checking the error from, and then another point where the error should be removed.
from contextlib import contextmanager
global_errors = {}
#contextmanager
def error_handler_context(error, function):
# Code to acquire resource, e.g.:
global_errors[error] = function
try:
yield
finally:
# Code to release resource, e.g.:
del global_errors[error]
def handle_errors(function):
try:
function()
except Exception as e:
try:
global_errors[type(e)]()
except Exception:
raise e
def error_1():
print("here")
def value_error_raise():
raise ValueError("Test")
def exception_raise():
raise Exception("test Error")
with error_handler_context(ValueError, error_1):
handle_errors(value_error_raise)
handle_errors(exception_raise)
This is not a perfect solution, and there are definitely a lot of cases where this should not be used. So use caution.

Separating except portion of a try/except into a function

I have a try/except where I repeat the except portion frequently in my code. This led me to believe that it would be better to separate the except portion into a function.
Below is my use case:
try:
...
except api.error.ReadError as e:
...
except api.error.APIConnectionError as e:
...
except Exception as e:
...
How would I go about separating this logic into a function so I can do something as simple as:
try:
...
except:
my_error_handling_function(e)
Just define the function:
def my_error_handling(e):
#Do stuff
...and pass in the exception object as the parameter:
try:
#some code
except Exception as e:
my_error_handling(e)
Using just a generic Exception type will allow you to have a single except clause and handle and test for different error types within your handling function.
In order to check for the name of the caught exception, you can get it by doing:
type(e).__name__
Which will print the name, such as ValueError, IOError, etc.
I would suggest refactoring your code so the try/except block is only present in a single location.
For instance, an API class with a send() method, seems like a reasonable candidate for containing the error handling logic you have described in your question.
Define your function:
def my_error_handling(e):
#Handle exception
And do what you're proposing:
try:
...
except Exception as e:
my_error_handling_function(e)
You can handle logic by getting the type of the exception 'e' within your function. See: python: How do I know what type of exception occurred?
If you don't like try-catch statement, you can use exception-decouple package and decorator.
from exception_decouple import redirect_exceptions
def my_error_handling(arg, e):
#Do stuff
#redirect_exceptions(my_error_handling, api.error.ReadError, api.error.APIConnectionError)
def foo(arg):
...

Get raised error in a class to main()

Hi im currently doing a program like this.
class MyError(Exception):
def __init__(self, text = "Correct")
self.text = text
def __str__(self):
return (self.kod)
class Atom(self):
.
.
.
try:
function()
else:
raise MyError("Incorrect use of function")
def main():
try:
a = Atom()
except:
# Here i want to print the error that was raised
What I think I understand is that the error is raised in an object created in Atom().
But I want to send it to my main program and do the print of the error MyError there.
Is it possible to do this and how should I write it so that the correct text of exception is printed since i will have several different error messages.
If i come to the except statement I would want to get the message "Incorrect use of function" printed.
It seems that you're pretty close:
class MyError(Exception):
def __init__(self, text = "Correct")
self.text = text
def __str__(self):
return (self.kod)
class Atom(self):
.
.
.
try:
function()
except: # try: ... else: raise ... seems a bit funky to me.
raise MyError("Incorrect use of function")
def main():
try:
a = Atom()
except Exception as err: # Possibly `except MyError as err` to be more specific
print err
The trick is that when you catch the error, you want to bind the exception instance to a name using the as clause. Then you can print it, look at it's attributes, re-raise or pretty much do anything you choose with it.
Please note that this code still isn't "clean". Generally, you want to limit exception handling as much as possible -- only catch exceptions that expect to see and that you know how to handle. Otherwise, you can sometimes mask hard to find bugs in your code. Because of this:
try:
do_something()
except:
...
is discouraged (it catches all sorts of things like KeyboardInterrupt and SystemExit) ... Instead:
try:
do_something()
except ExceptionIKnowHowToHandle:
...
is advised.
Firstly, never do a blank except. That will catch all errors, including things like KeyboardInterrupt - so you won't be able to ctrl-c out of your program. Here you should just catch MyError.
The except clause also allows you to assign the actual exception to a variable, which you can then print or do anything else with. So you can do:
try:
...
except MyError as e:
print e.text

Frequently repeated try/except in Python

Firstly, I'm not sure if my approach is proper, so I'm open to a variety of suggestions.
If try/except statements are frequently repeated in code, are there any good ways to shorten them or avoid fully writing them out?
try:
# Do similar thing
os.remove('/my/file')
except OSError, e:
# Same exception handing
pass
try:
# Do similar thing
os.chmod('/other/file', 0700)
except OSError, e:
#Same exception handling
pass
For example, for one line actions you could define a exception handling wrapper and then pass a lambda function:
def may_exist(func):
"Work with file which you are not sure if exists."""
try:
func()
except OSError, e:
# Same exception handling
pass
may_exist(lambda: os.remove('/my/file'))
may_exist(lambda: os.chmod('/other/file', 0700))
Does this 'solution' just make things less clear? Should I just fully write out all the try/except statements?
The best way to abstract exception handling is with a context manager:
from contextlib import contextmanager
#contextmanager
def common_handling():
try:
yield
finally:
# whatever your common handling is
then:
with common_handling():
os.remove('/my/file')
with common_handling():
os.chmod('/other/file', 0700)
This has the advantage that you can put full statements, and more than one of them, in each common_handling block.
Keep in mind though, your need to use the same handling over and over again feels a lot like over-handling exceptions. Are you sure you need to do this much?
It would probably be cleaner to make may_exist a decorator:
from functools import wraps
def may_exist(func):
#wraps(func):
def wrapper(*args, **kwds):
try:
return func(*args, **kwds)
except OSError:
pass
return wrapper
Then you can either do:
may_exist(os.remove)('/my/file')
may_exist(os.chmod)('/other/file', 0700)
for a one-off call, or:
remove_if_exists = may_exist(os.remove)
...
remove_if_exists('somefile')
if you use it a lot.
I think your generic solution is ok, but I wouldn't use those lambdas at the bottom. I'd recommend passing the function and arguments like this
def may_exist(func, *args):
"Work with file which you are not sure if exists."""
try:
func(args)
except OSError, e:
# Same exception handling
pass
may_exist(os.remove, '/my/file')
may_exist(os.chmod, '/other/file', '0700')
Would something like this work:
def may_exist(func, *func_args):
try:
func(*func_args)
except OSError as e:
pass

Python: How to tell the for loop to continue from a function?

Sometimes I need the following pattern within a for loop. At times more than once in the same loop:
try:
# attempt to do something that may diversely fail
except Exception as e:
logging.error(e)
continue
Now I don't see a nice way to wrap this in a function as it can not return continue:
def attempt(x):
try:
raise random.choice((ValueError, IndexError, TypeError))
except Exception as e:
logging.error(e)
# continue # syntax error: continue not properly in loop
# return continue # invalid syntax
return None # this sort of works
If I return None than I could:
a = attempt('to do something that may diversely fail')
if not a:
continue
But I don't feel that does it the justice. I want to tell the for loop to continue (or fake it) from within attempt function.
Python already has a very nice construct for doing just this and it doesn't use continue:
for i in range(10):
try:
r = 1.0 / (i % 2)
except Exception, e:
print(e)
else:
print(r)
I wouldn't nest any more than this, though, or your code will soon get very ugly.
In your case I would probably do something more like this as it is far easier to unit test the individual functions and flat is better than nested:
#!/usr/bin/env python
def something_that_may_raise(i):
return 1.0 / (i % 2)
def handle(e):
print("Exception: " + str(e))
def do_something_with(result):
print("No exception: " + str(result))
def wrap_process(i):
try:
result = something_that_may_raise(i)
except ZeroDivisionError, e:
handle(e)
except OverflowError, e:
handle(e) # Realistically, this will be a different handler...
else:
do_something_with(result)
for i in range(10):
wrap_process(i)
Remember to always catch specific exceptions. If you were not expecting a specific exception to be thrown, it is probably not safe to continue with your processing loop.
Edit following comments:
If you really don't want to handle the exceptions, which I still think is a bad idea, then catch all exceptions (except:) and instead of handle(e), just pass. At this point wrap_process() will end, skipping the else:-block where the real work is done, and you'll go to the next iteration of your for-loop.
Bear in mind, Errors should never pass silently.
The whole idea of exceptions is that they work across multiple levels of indirection, i.e., if you have an error (or any other exceptional state) deep inside your call hierarchy, you can still catch it on a higher level and handle it properly.
In your case, say you have a function attempt() which calls the functions attempt2() and attempt3() down the call hierarchy, and attempt3() may encounter an exceptional state which should cause the main loop to terminate:
class JustContinueException(Exception):
pass
for i in range(0,99):
try:
var = attempt() # calls attempt2() and attempt3() in turn
except JustContinueException:
continue # we don't need to log anything here
except Exception, e:
log(e)
continue
foo(bar)
def attempt3():
try:
# do something
except Exception, e:
# do something with e, if needed
raise # reraise exception, so we catch it downstream
You can even throw a dummy exception yourself, that would just cause the loop to terminate, and wouldn't even be logged.
def attempt3():
raise JustContinueException()
Apart from the context I just want to answer the question in a brief fashion. No, a function cannot continue a loop it may be called in. That is because it has no information about this context. Also, it would raise a whole new class of questions like what shall happen if that function is called without a surrounding loop to handle that continue?
BUT a function can signal by various means that it wants the caller to continue any loop it currently performs. One means of course is the return value. Return False or None to signal this for example. Another way of signaling this is to raise a special Exception:
class ContinuePlease(Exception): pass
def f():
raise ContinuePlease()
for i in range(10):
try:
f()
except ContinuePlease:
continue
Maybe you want to do continuations? You could go and look at how Eric Lippert explains them (if you are ready to have your mind blown, but in Python it could look a bit like this:
def attempt(operation, continuation):
try:
operation()
except:
log('operation failed!')
continuation()
Inside your loop you could do:
attempt(attempt_something, lambda: foo(bar)) # attempt_something is a function
You could use this:
for l in loop:
attempt() and foo(bar)
but you should make sure attempt() returns True or False.
Really, though, Johnsyweb's answer is probably better.
Think that you are mapping foo on all items where attempt worked. So attempt is a filter and it's easy to write this as a generator:
def attempted( items ):
for item in items:
try:
yield attempt( item )
except Exception, e:
log(e)
print [foo(bar) for bar in attempted( items )]
I wouldn't normally post a second answer, but this is an alternative approach if you really don't like my first answer.
Remember that a function can return a tuple.
#!/usr/bin/env python
def something_that_mail_fail(i):
failed = False
result = None
try:
result = 1.0 / (i % 4)
except:
failed = True # But we don't care
return failed, result
for i in range(20):
failed, result = something_that_mail_fail(i)
if failed:
continue
for rah in ['rah'] * 3:
print(rah)
print(result)
I maintain that try ... except ... else is the way to go, and you shouldn't silently ignore errors though. Caveat emptor and all that.
Try the for loop outside the try, except block
This answer had Python 3.4 in mind however there are better ways in newer versions. Here is my suggestion
import sys
if '3.4' in sys.version:
from termcolor import colored
def list_attributes(module_name):
'''Import the module before calling this func on it.s '''
for index, method in enumerate(dir(module_name)):
try:
method = str(method)
module = 'email'
expression = module + '.' + method
print('*' * len(expression), '\n')
print( str(index).upper() + '. ',colored( expression.upper(), 'red'),
' ', eval( expression ).dir() , '...' , '\n'2 )
print('' * len(expression), '\n')
print( eval( expression + '.doc' ), '\n'*4,
'END OF DESCRIPTION FOR: ' + expression.upper(), '\n'*4)
except (AttributeError, NameError):
continue
else:
pass
finally:
pass
Edit: Removed all that stupidity I said...
The final answer was to rewrite the whole thing, so that I don't need to code like that.

Categories

Resources