How do I make a contextmanager with a loop inside? - python

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

Related

AssertError not catched in unittest in python try-except clause

I have a object created in a test case, and want to make test inside of its method.
But the exception is swallow by the try-except clause.
I know I can change raise the exception in run but it is not what I want. Is there a way that any unittest tool can handle this?
It seems that assertTrue method of unittest.TestCase is just a trivial assert clause.
class TestDemo(unittest.TestCase):
def test_a(self):
test_case = self
class NestedProc:
def method1(self):
print("flag show the method is running")
test_case.assertTrue(False)
def run(self):
try:
self.method1()
except:
pass # can raise here to give the exception but not what I want.
NestedProc().run() # no exception raised
# NestedProc().method1() # exception raised
EDIT
For clarity, I paste my realworld test case here. The most tricky thing here is that ParentProcess will always success leading to AssertError not correctly being propagated to test function.
class TestProcess(unittest.TestCase);
#pytest.mark.asyncio
async def test_process_stack_multiple(self):
"""
Run multiple and nested processes to make sure the process stack is always correct
"""
expect_true = []
def test_nested(process):
expect_true.append(process == Process.current())
class StackTest(plumpy.Process):
def run(self):
# TODO: unexpected behaviour here
# if assert error happend here not raise
# it will be handled by try except clause in process
# is there better way to handle this?
expect_true.append(self == Process.current())
test_nested(self)
class ParentProcess(plumpy.Process):
def run(self):
expect_true.append(self == Process.current())
proc = StackTest()
# launch the inner process
asyncio.ensure_future(proc.step_until_terminated())
to_run = []
for _ in range(100):
proc = ParentProcess()
to_run.append(proc)
await asyncio.gather(*[p.step_until_terminated() for p in to_run])
for proc in to_run:
self.assertEqual(plumpy.ProcessState.FINISHED, proc.state)
for res in expect_true:
self.assertTrue(res)
Any assert* method and even fail() just raises an exception. The easiest method is probably to manually set a flag and fail() afterwards:
def test_a(self):
success = True
class NestedProc:
def method1(self):
nonlocal success
success = False
raise Exception()
...
NestedProc().run()
if not success:
self.fail()

Catching exception in decorator that wraps generator

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

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.

Slow execution of customized is_element_present

I have in method body of another method:
for i in range(60):
try:
if sel.is_element_present("//div[#id='result']/form[3]/strong/div/button"): break
except: pass
time.sleep(1)
and it executes in 5 seconds.
Nothing changes on site and I execute this line:
self.WaitForElement(u"//div[#id='result']/form[3]/strong/div/button")
def WaitForElement(self,name):
for i in range(60):
try:
if sel.is_element_present(name): break
except: pass
time.sleep(1)
and it executes in almost 30 seconds, so it's very weird.
Do you have any idea?
Looking quickly over your code, the only thing I can see is you defined a standard string in the first example but a unicode string in the second.
Here is what I have for the is_element_present method:
def is_element_present(self, how, what):
try: self.driver.find_element(by=how, value=what)
except NoSuchElementException, e: return False
return True
Since it is already doing a try...except block, you don't need to wrap it in try...except again. Since is_element_present will only return True or False, the except: pass part of your code will probably never be executed. Instead, just use the if statement.
I also think you have a typo in your code ("sel" instead of "self").

Categories

Resources