Execute code block if condition or exception - python-3.x

With exceptions being so central to idiomatic Python, is there a clean way to execute a particular code block if an expression evaluates to True or the evaluation of the expression raises an exception? By clean, I mean an easy-to-read, Pythonic, and not repeating the code block?
For example, instead of:
try:
if some_function(data) is None:
report_error('Something happened')
except SomeException:
report_error('Something happened') # repeated code
can this be cleanly rewritten so that report_error() isn't written twice?
(Similar question: How can I execute same code for a condition in try block without repeating code in except clause but this is for a specific case where the exception can be avoided by a simple test within the if statement.)

Yes, this can be done relatively cleanly, although whether it can be considered good style is an open question.
def test(expression, exception_list, on_exception):
try:
return expression()
except exception_list:
return on_exception
if test(lambda: some_function(data), SomeException, None) is None:
report_error('Something happened')
This comes from an idea in the rejected PEP 463.
lambda to the Rescue presents the same idea.

Related

is it mandatory to have if-else block at same corresponding level(one below other) in python3?

I am a python newbie and I have a confusion in writing this if-else block with correct indentation. I read that if-else block should appear at same corresponding level(one below other)however when I was writing a code block, the code block still worked by giving me the output. So, my question is that is it mandatory to write if-else code block at same corresponding vertical level(one below other)? if No, then can you explain, how does the logic flow in the below code?
The code gives the prime number within a list.
h=9
a=[]
for i in range(2,h):
for j in range(2,i):
if i%j==0:
break
else:
a.append(i)
The above code worked without having the correct indentation.
Short answer, yes indention is mandatory.
Maybe you can try list comprehension so you can attempt your code in a single line.

in python, is it safe to return from within a "with" block?

in the following example code:
with open(filename,"r") as file:
return file.read()
does the return statement cause any potential instability?
does the "with" operator still provide its safeguards under these circumstances?
since the return happens, and nothing is executed after the return statement, does the with operator still perform its closing operations that ensure the stability of the file operations and close file pointers?
EDIT:
This seems to answer the question:
In Python, if I return inside a "with" block, will the file still close?

Raise statements: Why does my block of code return an empty list instead of executing my "raise" statement?

Firstly, I apologise if my question comes across as stupid or if there are any errors in my post - This is my second post, and I am still finding it difficult how to ask appropriate questions on this platform.
Secondly, I have tried to look up the logic behind the "raise" statement using the examples from the Python Docs website (https://docs.python.org/3/tutorial/errors.html) and another post on Stackoverflow (Raise statement) but I still cannot wrap my head around this "raise" statement.
I am trying to answer the following question from the Ebook Introduction to computation and programming using Python: "Implement a function that satisfies the specification." (See function specification below).
def findAnEven(L):
"""
ASSUMES: L is a list of integers
RETURNS: the first even number in L
Raises ValueError if L does not contain an even number
"""
Below is my attempt to solve this question:
def findAnEven(L):
"""
ASSUMES: L is a list of integers
RETURNS: the first even number in L
Raises ValueError if L does not contain an even number
"""
try:
evenNumbers = []
for i in L:
if i % 2 == 0:
evenNumbers.append(i)
break
return(evenNumbers)
except ValueError:
raise ValueError("ValueError exception thrown")
findAnEven([1,2,3,4]) # RETURNS: [2]
findAnEven([1,1,1,1]) # RETURNS: [] - THIS SHOULD EXECUTE RAISE STATEMENT, NOT RETURN AN EMPTY LIST
I am trying to understand why my block of code is only returning an empty list, instead of executing my "raise" statement?
If anyone can explain (in layman terms) why my block of code is returning an empty list to the last function call (i.e., findAnEven([1,1,1,1]) instead of executing my "raise" statement, I would be very grateful - Again apologies for the stupid question!
try:
evenNumbers = []
for i in L:
if i % 2 == 0:
evenNumbers.append(i)
break
return(evenNumbers)
except ValueError:
raise ValueError("ValueError exception thrown")
The code in the except block will never be executed since no line in the try block can ever raise a ValueError. In case there are no even numbers in L, the if is never True and return(evenNumbers) just returns the empty evenNumbers list.
If you want to raise an exception instead of returning an empty list, do that explicitly.
I also took the liberty to make your code more pythonic and succinct.
Once it finds the first even number it will return a list that contains it.
If the return statement is never hit, that means no even number was found, so we raise ValueError with a clear message.
def find_an_even(L):
"""
ASSUMES: L is a list of integers
RETURNS: the first even number in L
Raises ValueError if L does not contain an even number
"""
for n in L:
if n % 2 == 0:
return [n]
# or just 'return n' if you want to return the number not within a list
raise ValueError("No even numbers in L")
First off, there are no stupid questions! Everyone learns at their own pace.
Python's raise is part of a series of Error related keywords in Python (see full list here). The most important ones you should know are try, except, and raise. I'll explain each of them here:
try is always paired with except, as you did here. What happens is that whatever is "in scope" for the try will execute just like any normal block of code would. However, if something bad happens (i.e your code makes a bad assumption or something breaks) and a ValueError is raised, the script does not immediately terminate or stop executing - instead, it "bubbles up" that error to the except clause immediately below, and allows you to handle it instead, just as you did here. However, the stuff in the except clause only gets executed if a ValueError is ACTUALLY RAISED while executing the try clause. If the code in the try clause executes without any issues, then the except clause's code is never executed - it is skipped entirely.
Once the issue is bubbled up to the except clause, you can have it print something or do whatever you want, then force it to actually halt execution by using the raise keyword, which artificially creates an Error inside your Error handler (i.e the except clause).
In your example, a ValueError never gets raised anywhere (since your code does not break), so the except clause's code is never called! I think you're conflating an "Error" with something just being False. In your example, findAnEven([1,1,1,1]) executes perfectly - it returns an empty list, since there are indeed no even numbers in the list you passed. The fact that there are no even numbers is not an Error - it's just a fact about your inputted list.

Handle pytest cases where you want to trigger actions based on pass or fail of a testcase

I am running test cases using pytest; although I would like to collect some logs and zip everything, only if there is a failure
I did write the function that collect logs and zip it, although I can't find a way to actually trigger that for each failed case. I did implement a solution using the #classmethod decorators for setUpClass and tearDownClass; but this happen only after all the tests have been ran, and not when a specific test fail.
In each test I run a simple assertFalse(a < b, "the test did fail". I would like to trigger the function that does the log collection only when that condition is actually triggered.
Seems that there is no direct way to do it in the assert statement, or I was not able to find a way for it.
I just used try-catch and in the try I raise an exception,and in the catch code I call the function. Not sure if this is the best way, but it works in my case
try:
if (a < b):
raise ValueError ("wrong output, collecting logs")
except ValueError:
# call function to parse logs
self.parseoutputonerror(a, b, "/tmp/out.zip")

How to catch any exception but a group of exceptions in python 3

What might be a pythonic way of capturing all exceptions except for a few like SyntaxError or AssertionError which you'd want to always go uncaught?
Motivation being that we want to iterate a huge number of input files, and use try to gracefully go on when just one or few files fail to be processed. Catching SyntaxError, AssertionError and such would be too much to continue running after, as they imply a level of code breakage much higher than anything we'd want to forgo.
At a deeper level, is there beyond python 3's Exception hierarchy, any existing grouping of exceptions that better differentiates exception types? I do not find the given hierarchy extremely sensible.
How about this:
try:
# do some stuff here
except SyntaxError:
# do not catch this one
raise
except AssertionError:
# do not catch this one
raise
except Exception:
# catch all the others
What about catching the exceptions broadly and then check if the caught exception is what you want to catch.
enter code here
try:
#code
except Exception:
if Exception == AssertionError:
# do smthg
elseif Exception == SyntaxError:
# do smthg
else:
return

Resources