I have the next code and it works well.
class AggregateException(Exception):
pass
class Class(object):
def some_method(self):
...
try:
if aggregate(count):
status = Checked
except AggregateException:
status = Rejected
...
def aggregate(count):
if <condition>:
raise AggregateException('Invalid count')
But when I try to simplify logic of some_method() I get an unexpected result. Below you can see code
class Class(object):
def some_method(self):
...
self.aggregate_count(count)
...
def aggregate_count(count):
try:
if aggregate(count):
status = Checked
except AggregateException:
status = Rejected
When an error occurs in aggregate_count(), some_method() continues to run instead finish.
I'm not entirely sure what do you want to do. But as far as I understood you'd like to keep the program running if an exception occur. And that's what your script is doing. The exceptstatement catches your exception and preventing your program from crashing (and change status). Thereafter the rest of the some_method-function is executed. If you want to see it on the command line simply print it in the except block.
If you'd like to crash your program and print the exception to the command line, remove the try and except statement.
Hope that helps.
Related
First of all, sorry for the wording of the question, I can't express it in a more compact form.
Let's say I have a code like this in Python:
something_happened = False
def main():
# 'procs' is a list of procedures
for proc in procs:
try:
# Any of these can set the 'something_happened'
# global var to True
proc()
except as e:
handle_unexpected_exception(e)
continue
# If some procedure found some problem,
# print a remainder to check the logging files
if something_happened:
print('Check the logfile, just in case.')
Any of the involved procedures may encounter some problem but execution MUST continue, the problem is properly logged and that's the ONLY handling needed, really, because the problems that may arise while running the procedures shouldn't stop the program, this shouldn't involve raising an exception and stopping the execution.
The reason why the logfile should be checked is that some of the problems may need further human action, but the program can't do anything about them, other than logging them and keep running (long story).
Right now the only way of achieving this that I can think about is to make each procedure to set something_happened == True after logging a potential problem, but using a global variable which may be set from any of the procedures, or returning a status code from the procedures.
And yes, I know I can raise an exception from the procedures instead of setting a global or returning an error code, but that would only work because I'm running them in a loop, and this may change in the future (and then raising an exception will jump out the try-block), so that's my last resort.
Can anyone suggest a better way of dealing with this situation? Yes, I know, this is a very particular use case, but that's the reason why I'm not raising an exception in the first place, and I'm just curious because I didn't find anything after googling for hours...
Thanks in advance :)
You have a variable that may be set to True by any of the procs. It looks like a common OOP schema:
class A():
"""Don't do that"""
def __init__(self, logger):
self._logger = logger
self._something_happened = False
def proc1(self):
try:
...
except KeyError as e:
self._something_happened = True
self._logger.log(...)
def proc2(self):
...
def execute(self):
for proc in [self.proc1, self.proc2, ...]:
try:
proc()
except as e:
self._handle_unexpected_exception(e)
continue
if self._something_happened:
print('Check the logfile, just in case.')
But that's a very bad idea, because you're violating the Single Responsibility Principle: your classs has to know about proc1, proc2, ... You have to reverse the idea:
class Context:
def __init__(self):
self.something_happened = False
def main():
ctx = Context()
for proc in procs:
try:
proc(ctx) # proc may set ctx.something_happened to True
except as e:
handle_unexpected_exception(e)
continue
if ctx.something_happened:
print('Check the logfile, just in case.')
Creating a void class like that is not attracting. You can take the idea further:
class Context:
def __init__(self, logger):
self._logger = logger
self._something_happened = False
def handle_err(self, e):
self._something_happened = True
self._logger.log(...)
def handle_unexpected_exception(self, e):
...
self._logger.log(...)
def after(self):
if self._something_happened:
print('Check the logfile, just in case.')
def proc1(ctx):
try:
...
except KeyError as e:
ctx.handle_err(e) # you delegate the error handling to ctx
def proc2(ctx):
...
def main():
ctx = Context(logging.gerLogger("main"))
for proc in procs:
try:
proc(ctx)
except as e:
ctx.handle_unexpected_exception(e)
ctx.after()
The main benefit here is you that can use another Context if you want:
def StrictContext():
def handle_err(self, e):
raise e
def handle_unexpected_exception(self, e):
raise e
def after(self):
pass
Or
class LooseContext:
def handle_err(self, e):
pass
def handle_unexpected_exception(self, e):
pass
def after(self):
pass
Or whatever you need.
Looks like the cleaner solution is to raise an exception, and I will change the code accordingly. They only problem is what will happen if in the future the loop goes away, but I suppose I'll cross that bridge when I arrive to it ;) and then I'll use another solution or I'll try to change the main code miself.
#cglacet, #Phydeaux, thanks for your help and suggestions.
#!/usr/bin/env python
class Functions() :
def A(self):
print "hey"
self.B()
return 1
def B(self):
print "hello"
exit(0)
func_obj = Functions()
def main() :
A = func_obj.A()
print A
print "awesome"
if __name__ == '__main__' :
main()
Above is my code. What I'm trying to do is that I want to call functionA from the main() function and when functionA executes functionB, I want functionB to raise error and exit back to the main() function without going back to functionA. How can I achieve this? Basically I want the main function to print "awesome" after functionB exits. I'm not sure what is the correct keyword to look it up.
What you're looking for are exceptions - they are actually designed to do just this: break the normal flow of execution and propagate up the call stack until someone take care of them (as a last resort, the runtime will catch them, display the error message and a full traceback, and exit).
There are two parts to the process: first raising the exception, then catching it at the right place. In your example it might look like:
# declare our own exception type so we can catch specifically this one
class MyOwnException(Exception):
pass
def a():
print("in a - before b")
b()
print("in a - after b (shouldn't see this)")
def b():
print("in b, before raise")
raise MyOwnException("hello houston ?")
print("in b, after raise (shouldn't see this)")
if __name__ == "__main__":
print("calling a")
try:
a()
print("after a (shouldn't see this)")
except MyOwnException as e:
print("caugth error {}".format(e))
FWIW, your example using exit() was really close since exit() actually works by raising a SysExit exception. The first and main use case for exception is of course error handling, but it's actually really a way to control the execution flow of your program (as an example the StopIteration exception is used to signal an exhausted iterator).
Hey so after someone pointed out my original answer didnt work i went searching! you can create custom exception classes to achieve what you're looking for!
class HaltException(Exception):
pass
class Functions():
def a(self):
print("hey")
self.b()
return "1"
def b(self):
print("hello")
raise HaltException("This is an exception error.")
def main():
func_obj = Functions()
try:
func_obj.a()
except HaltException as error:
print(error)
print("Awesome")
if __name__ == "__main__":
main()
This would then return the following when run :
hey
hello
This is an exception error.
Awesome
I have a decorator that wraps a generator that yields from inside a nose test case. For every iteration, I'm looking to catch and run a specific teardown if an exception occurs, however it does not seem to behave as expected.
def print_log(test_case):
#wraps(test_case)
def run_test(self):
try:
for _ in test_case(self): pass
except:
Test_Loop.failure_teardown(self)
raise
return run_test
Is there something I am doing wrong?
I'm not sure exactly what the unexpected behavior is, but maybe it is happening because you are not trying each loop iteration individually.
Maybe this will work?
def print_log(test_case):
#wraps(test_case)
def run_test(self):
from six.moves import next
test_iter = iter(test_case(self))
while True:
try:
next(test_iter)
except StopIteration:
break
except Exception:
Test_Loop.failure_teardown(self)
raise
return run_test
I'm writing a program in Python, and nearly every method im my class is written like this:
def someMethod(self):
try:
#...
except someException:
#in case of exception, do something here
#e.g display a dialog box to inform the user
#that he has done something wrong
As the class grows, it is a little bit annoying to write the same try-except block over and over. Is it possible to create some sort of 'global' exception for the whole class? What's the recommended way in Python to deal with this?
Write one or more exception handler functions that, given a function and the exception raised in it, does what you want to do (e.g. displays an alert). If you need more than one, write them.
def message(func, e):
print "Exception", type(e).__name__, "in", func.__name__
print str(e)
Now write a decorator that applies a given handler to a called function:
import functools
def handle_with(handler, *exceptions):
try:
handler, cleanup = handler
except TypeError:
cleanup = lambda f, e: None
def decorator(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except exceptions or Exception as e:
return handler(func, e)
else:
e = None
finally:
cleanup(func, e)
return wrapper
return decorator
This only captures the exceptions you specify. If you don't specify any, Exception is caught. Additionally, the first argument can be a tuple (or other sequence) of two handler functions; the second handler, if given, is called in a finally clause. The value returned from the primary handler is returned as the value of the function call.
Now, given the above, you can write:
#handle_with(message, TypeError, ValueError)
def add(x, y):
return x + y
You could also do this with a context manager:
from contextlib import contextmanager
#contextmanager
def handler(handler, *exceptions):
try:
handler, cleanup = handler
except TypeError:
cleanup = lambda e: None
try:
yield
except exceptions or Exception as e:
handler(e)
else:
e = None
finally:
cleanup(e)
Now you can write:
def message(e):
print "Exception", type(e).__name__
print str(e)
def add(x, y):
with handler(message, TypeError, ValueError):
return x + y
Note that the context manager doesn't know what function it's in (you can find this out, sorta, using inspect, though this is "magic" so I didn't do it) so it gives you a little less useful information. Also, the context manager doesn't give you the opportunity to return anything in your handler.
I can think of two options:
Write a decorator that can wrap each method in the try block.
Write a "dispatcher" method that calls the appropriate method inside a try block, then call that method instead of the individual ones. That is, instead of calling obj.someMethod(), obj.otherMethod, you call obj.dispatch('someMethod') or obj.dispatch('otherMethod'), where dispatch is a wrapper that contains the try block.
Your approach seems like a bit of a strange design, though. It might make more sense to have the dialog-box stuff in some other part of the code, some higher-level event loop that catches errors and displays messages about them.
In my code, I need to be able to open and close a device properly, and therefore see the need to use a context manager. While a context manager is usually defined as a class with __enter__ and __exit__ methods, there also seem to be the possibility to decorate a function for use with the context manager (see a recent post and another nice example here).
In the following (working) code snippet, I have implemented the two possibilities; one just need to swap the commented line with the other one:
import time
import contextlib
def device():
return 42
#contextlib.contextmanager
def wrap():
print("open")
yield device
print("close")
return
class Wrap(object):
def __enter__(self):
print("open")
return device
def __exit__(self, type, value, traceback):
print("close")
#with wrap() as mydevice:
with Wrap() as mydevice:
while True:
time.sleep(1)
print mydevice()
What I try is to run the code and stop it with CTRL-C. When I use the Wrap class in the context manager, the __exit__ method is called as expeced (the text 'close' is printed in the terminal), but when I try the same thing with the wrap function, the text 'close' is not printed to the terminal.
My question: Is there a problem with the code snippet, am I missing something, or why is the line print("close") not called with the decorated function?
The example in the documentation for contextmanager is somewhat misleading. The portion of the function after yield does not really correspond to the __exit__ of the context manager protocol. The key point in the documentation is this:
If an unhandled exception occurs in the block, it is reraised inside the generator at the point where the yield occurred. Thus, you can use a try...except...finally statement to trap the error (if any), or ensure that some cleanup takes place.
So if you want to handle an exception in your contextmanager-decorated function, you need to write your own try that wraps the yield and handle the exceptions yourself, executing cleanup code in a finally (or just block the exception in except and execute your cleanup after the try/except). For example:
#contextlib.contextmanager
def cm():
print "before"
exc = None
try:
yield
except Exception, exc:
print "Exception was caught"
print "after"
if exc is not None:
raise exc
>>> with cm():
... print "Hi!"
before
Hi!
after
>>> with cm():
... print "Hi!"
... 1/0
before
Hi!
Exception was caught
after
This page also shows an instructive example.