Create exceptional code
Never assume your code users will always do what you expect. Darren Yates looks at how to make your Python apps ‘fail gracefully’ using exceptions.
Writing your own code can be a lot of fun — after all, you’re developing a product or service you or others will use. The only problem is, you can’t always predict what your end-users will do. For example, you may require a user to type in an ID number. What happens if a number entered is outside the index range? What if it’s a negative number or a word? If they try to open up a file that doesn’t exist, what happens next? If not catered for, these examples of user input in these particular applications would likely cause app failure. Exception handling is the technique of pre-empting input problems in a way that’s often termed ‘handled gracefully’. It’s about maximising the user experience (UX) of your code and Python has built-in functions to make this easier to achieve.
SIMPLE STEPS
When you’re coding for input from your users, the first step is to think about the type of input you’re after. For example, say you have an app that returns the square-root of a decimal or ‘floating-point’ number (grab our source code from apcmag.com/magstuff and load up ‘squareroot.py’):
IMPORT MATH
while(True): num1text = input(“Enter a number to get square-root: “) print(math.sqrt (float(num1text)))
This simple code will indeed give you the square-root of a number, but it’s also important to know the range of input for which this function is valid, or in other words, the ‘input domain’. In mathematics, the square-root function is only valid for integers or floating-point numbers greater than or equal to zero (we won’t consider complex numbers here), so that’s the input domain for this function.
However, our code has problems. For starters, the input is a string — that in itself isn’t wrong, but what happens if the user types in a word instead of a number? Or enters nothing but the Enter key? The input function will be fine, but the float() function inside the math.sqrt() function will crash because there is no way to convert the string ‘two’, for example, into a floating-point number.
ISDECIMAL?
Interestingly, while Python comes with a huge range of mathematical functions, one function it doesn’t have is to identify if a string of text is actually a floating-point number. Python does have an isnumeric() string function, but it only works with digits 0 to 9 and doesn’t support a decimalpoint. The solution is: def isdecimal(number): try: float(number) return True except ValueError:
return False
This common solution features the ‘try/except’ statement, which is a key Python feature allowing you to capture specific errors that may occur in the execution of your code and to handle them ‘with grace’ rather than the full dummy-spit of a program crash. Here’s how it works — inside the ‘try’ clause, you put the code you want Python to at least attempt to execute. If the code works, open the champage. If the code fails, the ‘except’ clause allows you to recover the situation.
“Writing your own code can be fun, but you can’t predict what end users will do.”
What’s more, the ‘except’ clause can handle specific errors separately.
In our isdecimal() function, we start with just the float(number) statement. Yep, it looks odd on its own like that, but we’re not really interested in the result yet — we just want to know if the value of the ‘number’ parameter in the isdecimal() function header is a genuine floating-point number. If the float() function works, the value in ‘number’ is valid and we then return the Boolean value of ‘True’. However, if float(number) fails — for instance, it has alphabet characters or an empty string — the float() function immediately fires off a ‘ValueError’ flag, which is caught by the except clause. Since we know at this point the value of ‘number’ isn’t a floating-point number, we return ‘False’.
SQUARE-ROOT FIX
We can now combine this with Python’s math.sqrt() function, accounting not only for valid float-point numbers, but also add in checking for numbers greater than or equal to zero. You can see this in the source code ‘squareroot_b.py’. Sure, there’s more code — but it shouldn’t blow up in the user’s face.
CATCH ALL ERRORS
If you’re not particularly worried about which error type is picked up, you can skip the error type in the except clause — instead of ‘except ValueError:’ as we’ve coded, you just use ‘except:’. It’s a bit like a catch-all ‘else:’ clause in an