I have the following code.
MAXIMUM_DAYS = 7
def foo(start_date = None, end_date = None):
if abs((parse(start_date) - parse(end_date)).days) > MAXIMUM_DAYS:
return ?
How do I write the function in such a way that it exits/errors and I can test for that behavior?
I see the AssertRaises method in unittest. Is it as simple as creating a custom exception and raising it in the function, then testing for it in the unit test?
If so, are there standards about how and where to write the exception?
Is there a good example of this somewhere in the documentation or online?
Thanks,
Shawn
You are on the right track - using an exception is a common technique for what you have in mind. assert throws an exception, but it is not typically used in production code because assert statements can be disable on the command line. Instead, for your case raising a ValueError assertion would be a good choice:
MAXIMUM_DAYS = 7
def foo(start_date = None, end_date = None):
if abs((parse(start_date) - parse(end_date)).days) > MAXIMUM_DAYS:
raise ValueError(f'Date range exceeds max: {MAXIMUM_DAYS}')
Caveat... my assumption is that you are asking how to raise an exception if the value of your calculation is greater than MAXIMUM_DAYS. Based upon that assumption here is my answer.
Start by defining your exception:
class GreaterThanMaximumDays(Exception):
pass
Then simply in your code
MAXIMUM_DAYS = 7
def foo(start_date = None, end_date = None):
start_date_value = parse(start_date)
end_date_value = parse(end_date)
date_difference = abs(start_date_value - end_date_value)
if date_difference > MAXIMUM_DAYS:
raise GreaterThanMaximumDays()
return date_difference
Your guess as to if it is this easy and as easy as using AssertRaises is correct.
Personally, I like defining my own exceptions so this way I can easily test for its existence in my exception handling. I find it makes my code easier to read and understand exactly what I expect to be valid.
I pulled out the parsed values into their own variables. This simply makes debugging easier if you need to inspect the returned values for any reason at all.
Related
I have been told to design a new API in my company, and I am facing a dilemma when it comes to coding practices.
My API have to do several checks before they can be run, and often require multiple levels of functions to run.
Everything is fine until here. But most of my check (sub to sub to sub) function require the main API to return, without doing anything. Almost all of my check function have to return some data which is used by the next check function, and that is where my issue is. Because of this kind of structure, I have to return a status at the end of every check function along with the processed data, and after the function is called, I have to check the status before going to the next function.
Sample code:
def check1a():
if some_process():
return True, data_positive
return False, data_negative
#data_positive and data_negative cannot be used to identify whether the check passed or not.
def check1():
stats,data = check1a()
if not status:
return False, data
status, data = check1b(data)
if not status:
return False, data
status, data = check1c(data)
if not status:
return False, data
return status, data
def mainAPI():
status, data = check1(data)
if not status:
return data
status, data = check2(data)
if not status:
return data
status, data = check3()
if not status:
return "Failed"
return data
Being a religious follower of the "DRY" concept, if feel using exceptions to run the code in the following manner would be best.
def check1a():
if some_process():
return data_positive
exception1a = Exception("Error in check 1 a")
exception.data = data_negative
raise exception
def check1():
data = check1a()
data = check1b(data)
data = check1c(data)
return data
def mainAPI():
try:
data = check1(data)
data = check2(data)
data = check3(data)
return data
except Exception as e:
return e.data #I know exceptions don't always have data, but this is an illustration of what I think I should implement
Unfortunately raising an exception in the code to do implement this kind of working is kind of shunned upon at my company.
So here are my questions.
Is it really wrong to use Exceptions in this manner?
Is there a known downside to using Exceptions in this manner?
Is there a pythonic (or even a general coding) method which allows me to implement my code, and does not require me to stop following DRY.
This may not be a great answer, someone else way be able to help more.
Try-Exception:
This an opinion based topic. If they say they don't like you using try exception like this thn they probably don't believe in the "Better ask forgiveness than permission" principle.
That being said, throwing an Exception isn't bad; HOWEVER catching a general Exception is considered bad. If a piece of software is not running as desired (i.e. in some unknown way) you want it to fail thus you should only catch the specific Exception you want to catch.
You can find a an ample list of viable exceptions here, just pick one that seems reasonable and use it: Python Programming Exceptions
If you don't want catch one of the preexisting exceptions you can always make your own:
class MyAPIException(Exception):
def __init___(self, val):
self.val = val
Exception.__init__(self, "APIException with with arguments {0}".format(self.val))
def do_stuff(a,b,c):
raise MyAPIException({
'a' : a,
'b' : b,
'c' : c,
})
try:
do_stuff(1, 2, 3)
except MyAPIException as e:
print("API Exception:", e)
Alternative:
Another way you could help with DRY could be to use a list to make your calls.
def check1():
# List of functions you want to call in order
calls = [check1a, check1b, check1c]
for index, call in enumerate(calls):
# If it is the first function we will not pass any data
status, data = call() if index == 0 else call(data)
if not status:
return False, data
return status, data
This implementation also makes it easy to implement it as a generator if you wanted to return the result of each function call.
The answer by Error - Syntactical Remorse is a good one. Use exceptions that are defined by your API to handle control flow. To further expand on the answer, your API does not need to expose such exceptions, they can be caught by your internal functions as part of the way to handle control flow:
class MyAPIException(Exception):
pass
class SensorMiscalibrated(MyAPIException):
pass
def mainAPI():
try:
data = check1(data)
data = check2(data)
data = check3(data)
return True, data
except SensorMiscalibrated as e:
return False, data
What's neat about this is that any other exception raised by check1, check2… regarding file permissions or process errors will just bubble up and be ignored by the exception handler.
Exceptions are encouraged in Python and they do not introduce a performance penalty unlike other languages that implement them in a different way.
Lets say I have a function myFunc defined as
def myFunc(value):
return value if isinstance(value, int) else None
Now wherever in my project I use myFunc the enclosing funciton should return automatically if the value returned from myFunc is None and should continue if some integer value is returned
For example:
def dumbFunc():
# some code
# goes here..
result = myFunc('foo')
# some code
# goes here..
This funciton should automatically behave like..
def dumbFunc():
# some code
# goes here..
result = myFunc('foo')
if not result:
return
# some code
# goes here..
PS - I don't know whether this thing even possible or not.
This is simply not possible.
Apart from exceptions, you cannot give a called function the ability to impact the control flow of the calling scope. So a function call foo() can never interrupt the control flow without throwing an exception. As a consumer of the function, you (the calling function) always have the responsibility yourself to handle such cases and decide about your own control flow.
And it is a very good idea to do it like that. Just the possibility that a function call might interrupt my control flow without having a possibility to react on it first sounds like a pure nightmare. Just alone for the ability to release and cleanup resources, it is very important that the control flow is not taken from me.
Exceptions are the notable exception from this, but of course this is a deeply rooted language feature which also still gives me the ability to act upon it (by catching exceptions, and even by having finally blocks to perform clean up tasks). Exceptions are deliberately not silent but very loud, so that interruptions from the deterministic control flow are clearly visible and have a minimum impact when properly handled.
But having a silent feature that does neither give any control nor feedback would be just a terrible idea.
If myFunc is used at 100 places in my project, everywhere I need to put an if condition after it.
If your code is like that that you could just return nothing from any function that calls myFunc without having to do anything, then either you are building an unrealistic fantasy project, or you simply are not aware of the implications this can have to the calling code of the functions that would be returned that way.
ok, I'll bite.
on the one hand, this isn't really possible. if you want to check something you have to have a line in your code that checks it.
there are a few ways you could achieve something like this, but i think you may have already found the best one.
you already have this function:
def myFunc(value):
return value if isinstance(value, int) else None
I would probably have done:
def myFunc(value):
return isinstance(value, int)
but either way you could use it:
def dumb_func():
value = do_something()
if myFunc(value):
return
do_more()
return value
alternately you could use try and except
I would raise a TypeError, seeing as that seems to be what you are checking:
def myFunc(value):
if not isinstance(value, int):
raise TypeError('myFunc found that {} is not an int'.format(value))
then you can use this as such
def dumb_func():
value = do_something()
try:
myFunc(value):
Except TypeError as e:
print e # some feedback that this has happened, but no error raised
return
do_more()
return value
for bonus points you could define a custom exception (which is safer because then when you catch that specific error you know it wasn't raised by anything else in your code, also if you did that you could be lazier eg:)
Class CustomTypeError(TypeError):
pass
def dumb_func():
try:
value = do_something()
myFunc(value):
do_more()
return value
Except CustomTypeError as e:
print e # some feedback that this has happened, but no error raised
return
but none of this gets around the fact that if you want to act based on the result of a test, you have to check that result.
Python has a ternary conditional operator, and the syntax you used is right, so this will work:
def myFunc(value):
return value if isinstance(value, int) else None
def dumbFunc():
print("Works?")
result = myFunc(5)
print(result)
dumbFunc()
Result:
Works?
5
I want the function to return automatically in that case
This is not possible. To do that, you have to check the return value of myFunc() and act upon it.
PS: You could do that with a goto statement, but Python, fortunately, doesn't support this functionality.
Does Python has a feature that allows one to evaluate a function or expression and if the evaluation fails (an exception is raised) return a default value.
Pseudo-code:
evaluator(function/expression, default_value)
The evaluator will try to execute the function or expression and return the result is the execution is successful, otherwise the default_value is returned.
I know I create a user defined function using try and except to achieve this but I want to know if the batteries are already included before going off and creating a custom solution.
In order to reuse code, you can create a decorating function (that accepts a default value) and decorate your functions with it:
def handle_exceptions(default):
def wrap(f):
def inner(*a):
try:
return f(*a)
except Exception, e:
return default
return inner
return wrap
Now let's see an example:
#handle_exceptions("Invalid Argument")
def test(num):
return 15/num
#handle_exceptions("Input should be Strings only!")
def test2(s1, s2):
return s2 in s1
print test(0) # "Invalid Argument"
print test(15) # 1
print test2("abc", "b") # True
print test2("abc", 1) # Input should be Strings only!
No, the standard way to do this is with try... except.
There is no mechanism to hide or suppress any generic exception within a function. I suspect many Python users would consider indiscriminate use of such a function to be un-Pythonic for a couple reasons:
It hides information about what particular exception occurred. (You might not want to handle all exceptions, since some could come from other libraries and indicate conditions that your program can't recover from, like running out of disk space.)
It hides the fact that an exception occurred at all; the default value returned in case of an exception might coincide with a valid non-default value. (Sometimes reasonable, sometimes not really so.)
One of the principles of the Pythonic philosophy, I believe, is that "explicit is better than implicit," so Python generally avoids automatic type casting and error recovery, which are features of more "implicit- friendly"languages like Perl.
Although the try... except form can be a bit verbose, in my opinion it has a lot of advantages in terms of clearly showing where an exception may occur and what the control flow is around that exception.
Could you please explain why does the followin function return "1" when there is an exception? What is the purpose of returning "1"?
def initialize():
"""
Starting point for the program.
"""
try:
go = Car()
print "Instance of Car created"
except KeyboardInterrupt:
return 1
It's a fairly common idiom in C to return a non-zero value in case of an error.
My hunch here is that the person who created this function was used to programming in C and unfamiliar with exception handling. It's impossible to tell without a larger code sample, but if I'm right, then there is probably some sort of error handling present where this function is called in the case that this function returns 1, or a non-zero value.
If this is the case, a more proper way to use the exception would be to either use raise to pass the exception upward to be handled else where, or handle the exception right there in the function.
In my opinion there's no need to do this, only if the Car() constructor takes too long and you want to deal with it once initialize() returns.
I have some test cases. The test cases rely on data which takes time to compute. To speed up testing, I've cached the data so that it doesn't have to be recomputed.
I now have foo(), which looks at the cached data. I can't tell ahead of time what it will look at, as that depends a lot on the test case.
If a test case fails cause it doesn't find the right cached data, I don't want it to fail - I want it to compute the data and then try again. I also don't know what exception in particular it will throw cause of missing data.
My code right now looks like this:
if cacheExists:
loadCache()
dataComputed = False
else:
calculateData()
dataComputed = True
try:
foo()
except:
if not dataComputed:
calculateData()
dataComputed = True
try:
foo()
except:
#error handling code
else:
#the same error handling code
What's the best way to re-structure this code?
I disagree with the key suggestion in the existing answers, which basically boils down to treating exceptions in Python as you would in, say, C++ or Java -- that's NOT the preferred style in Python, where often the good old idea that "it's better to ask forgiveness than permission" (attempt an operation and deal with the exception, if any, rather than obscuring your code's main flow and incurring overhead by thorough preliminary checks). I do agree with Gabriel that a bare except is hardly ever a good idea (unless all it does is some form of logging followed by a raise to let the exception propagate). So, say you have a tuple with all the exception types that you do expect and want to handle the same way, say:
expected_exceptions = KeyError, AttributeError, TypeError
and always use except expected_exceptions: rather than bare except:.
So, with that out of the way, one slightly less-repetitious approach to your needs is:
try:
foo1()
except expected_exceptions:
try:
if condition:
foobetter()
else:
raise
except expected_exceptions:
handleError()
A different approach is to use an auxiliary function to wrap the try/except logic:
def may_raise(expected_exceptions, somefunction, *a, **k):
try:
return False, somefunction(*a, **k)
except expected_exceptions:
return True, None
Such a helper may often come in useful in several different situations, so it's pretty common to have something like this somewhere in a project's "utilities" modules. Now, for your case (no arguments, no results) you could use:
failed, _ = may_raise(expected_exceptions, foo1)
if failed and condition:
failed, _ = may_raise(expected_exceptions, foobetter)
if failed:
handleError()
which I would argue is more linear and therefore simpler. The only issue with this general approach is that an auxiliary function such as may_raise does not FORCE you to deal in some way or other with exceptions, so you might just forget to do so (just like the use of return codes, instead of exceptions, to indicate errors, is prone to those return values mistakenly being ignored); so, use it sparingly...!-)
Using blanket exceptions isn't usually a great idea. What kind of Exception are you expecting there? Is it a KeyError, AttributeError, TypeError...
Once you've identified what type of error you're looking for you can use something like hasattr() or the in operator or many other things that will test for your condition before you have to deal with exceptions.
That way you can clean up your logic flow and save your exception handling for things that are really broken!
Sometimes there's no nice way to express a flow, it's just complicated. But here's a way to call foo() in only one place, and have the error handling in only one place:
if cacheExists:
loadCache()
dataComputed = False
else:
calculateData()
dataComputed = True
while True:
try:
foo()
break
except:
if not dataComputed:
calculateData()
dataComputed = True
continue
else:
#the error handling code
break
You may not like the loop, YMMV...
Or:
if cacheExists:
loadCache()
dataComputed = False
else:
calculateData()
dataComputed = True
done = False
while !done:
try:
foo()
done = True
except:
if not dataComputed:
calculateData()
dataComputed = True
continue
else:
#the error handling code
done = True
I like the alternative approach proposed by Alex Martelli.
What do you think about using a list of functions as argument of the may_raise. The functions would be executed until one succeed!
Here is the code
def foo(x):
raise Exception("Arrrgh!")
return 0
def foobetter(x):
print "Hello", x
return 1
def try_many(functions, expected_exceptions, *a, **k):
ret = None
for f in functions:
try:
ret = f(*a, **k)
except expected_exceptions, e:
print e
else:
break
return ret
print try_many((foo, foobetter), Exception, "World")
result is
Arrrgh!
Hello World
1
Is there a way to tell if you want to do foobetter() before making the call? If you get an exception it should be because something unexpected (exceptional!) happened. Don't use exceptions for flow control.