Catching exception in decorator that wraps generator - python

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

Related

Handling Timeout Error(python)

import multiprocessing.pool
import functools
import time
import sys
def timeout(max_timeout):
def timeout_decorator(item):
#functools.wraps(item)
def func_wrapper(*args, **kwargs):
pool = multiprocessing.pool.ThreadPool(processes=1)
async_result = pool.apply_async(item, args, kwargs)
return async_result.get(max_timeout)
return func_wrapper
return timeout_decorator
I got this code from SE. This raises the timeout error from system level.
How could i handle this error and do something else when error pops up
Please Note Am a newbie in python.
Thanks in advance
You can wrap an error, using a try/except :
try:
do_something_that_can_raise_error_X()
except X:
do_something_when_error_X_is_raised()
You need to wrap the code that might raise this exception in "try" and then except the exception that you expect.
from multiprocessing import TimeoutError
try:
# actions that raise TimeoutError
except TimeoutError:
# handle TimeoutError
else:
# (optional) actions for when there is no TimeoutError
finally:
# (optional) actions to perform in any case
See this example in the multiprocessing docs.
Note, that using except without listing precise exception types is allowed syntactically but is not a good idea.
Also, you might want to learn how to handle Exceptions in python, see any python tutorial.

How do I make a contextmanager with a loop inside?

I want something like this:
from contextlib import contextmanager
#contextmanager
def loop(seq):
for i in seq:
try:
do_setup(i)
yield # with body executes here
do_cleanup(i)
except CustomError as e:
print(e)
with loop([1,2,3]):
do_something_else()
do_whatever()
But contextmanager doesn't work because it expects the generator to yield exactly once.
The reason why I want this is because I basically want to make my own custom for loop. I have a modified IPython that is used to control test equipment. It's obviously a full Python REPL, but most of the time the user is just calling predefined functions (similar to Bash prompt), and the user is not expected to be a programmer or familiar with Python. There needs to be a way to loop over some arbitrary code with setup/cleanup and exception handling for each iteration, and it should be about as simple to type as the above with statement.
I think a generator works better here:
def loop(seq):
for i in seq:
try:
print('before')
yield i # with body executes here
print('after')
except CustomError as e:
print(e)
for i in loop([1,2,3]):
print(i)
print('code')
will give:
before
1
code
after
before
2
code
after
before
3
code
after
Python enters and exits a with block only once so you can't have logic int the enter / exit steps that would be done repeatedly.
A more complete answer, for if the exception might happen outside the generator:
from contextlib import contextmanager
class CustomError(RuntimeError):
pass
#contextmanager
def handle_custom_error():
try:
yield
except CustomError as e:
print(f"handled: {e}")
def loop(seq):
for i in seq:
try:
print('before')
if i == 0:
raise CustomError("inside generator")
yield i # for body executes here
print('after')
except CustomError as e:
print(f"handled: {e}")
#handle_custom_error()
def do_stuff(i):
if i == 1:
raise CustomError("inside do_stuff")
print(f"i = {i}")
for i in loop(range(3)):
do_stuff(i)
Output:
before
handled: inside generator
before
handled: inside do_stuff
after
before
i = 2
after

Python magic method to alter the way raising an object is handled

I was wondering, is there a simple magic method in python that allows customization of the behaviour of an exception-derived object when it is raised? I'm looking for something like __raise__ if that exists. If no such magic methods exist, is there any way I could do something like the following (it's just an example to prove my point):
class SpecialException(Exception):
def __raise__(self):
print('Error!')
raise SpecialException() #this is the part of the code that must stay
Is it possible?
I don't know about such magic method but even if it existed it is just some piece of code that gets executed before actually raising the exception object. Assuming that its a good practice to raise exception objects that are instantiated in-place you can put such code into the __init__ of the exception. Another workaround: instead of raising your exception directly you call an error handling method/function that executes special code and then finally raises an exception.
import time
from functools import wraps
def capture_exception(callback=None, *c_args, **c_kwargs):
"""捕获到异常后执行回调函数"""
assert callable(callback), "callback 必须是可执行对象"
def _out(func):
#wraps(func)
def _inner(*args, **kwargs):
try:
res = func(*args, **kwargs)
return res
except Exception as e:
callback(*c_args, **c_kwargs)
raise e
return _inner
return _out
def send_warning():
print("warning message..............")
class A(object):
#capture_exception(callback=send_warning)
def run(self):
print('run')
raise SystemError("测试异常捕获回调功能")
time.sleep(0.2)
if __name__ == '__main__':
a = A()
a.run()

Exceptions for the whole class

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.

generic exception handling and return arg on exception

I am trying to create generic exception handler - for where I can set an arg to return in case of exception, inspired from this answer.
import contextlib
#contextlib.contextmanager
def handler(default):
try:
yield
except Exception as e:
yield default
def main():
with handler(0):
return 1 / 0
with handler(0):
return 100 / 0
with handler(0):
return 'helllo + 'cheese'
But this results in
RuntimeError: generator didn't stop after throw()
The main conceptual problem is that you try to make the calling function implicitly return a value from within a called function. To give an example, what you are trying to do is coneptually equivalent to this situation:
def f():
# some magic code here
def g():
f()
And now you want the magic code to make g() return some value. This is never going to work.
Context managers are the wrong tool for this purpose. Consider using a decorator instead.

Categories

Resources