Python logic error

Errors or mistakes in a program are often referred to as bugs. They are almost always the fault of the programmer. The process of finding and eliminating errors is called debugging. Errors can be classified into three major groups:

Errors¶

Errors or mistakes in a program are often referred to as bugs. They are almost always the fault of the programmer. The process of finding and eliminating errors is called debugging. Errors can be classified into three major groups:

  • Syntax errors
  • Runtime errors
  • Logical errors

Syntax errors¶

Python will find these kinds of errors when it tries to parse your program, and exit with an error message without running anything. Syntax errors are mistakes in the use of the Python language, and are analogous to spelling or grammar mistakes in a language like English: for example, the sentence Would you some tea? does not make sense – it is missing a verb.

Common Python syntax errors include:

  • leaving out a keyword
  • putting a keyword in the wrong place
  • leaving out a symbol, such as a colon, comma or brackets
  • misspelling a keyword
  • incorrect indentation
  • empty block

Note

it is illegal for any block (like an if body, or the body of a function) to be left completely empty. If you want a block to do nothing, you can use the pass statement inside the block.

Python will do its best to tell you where the error is located, but sometimes its messages can be misleading: for example, if you forget to escape a quotation mark inside a string you may get a syntax error referring to a place later in your code, even though that is not the real source of the problem. If you can’t see anything wrong on the line specified in the error message, try backtracking through the previous few lines. As you program more, you will get better at identifying and fixing errors.

Here are some examples of syntax errors in Python:

myfunction(x, y):
    return x + y

else:
    print("Hello!")

if mark >= 50
    print("You passed!")

if arriving:
    print("Hi!")
esle:
    print("Bye!")

if flag:
print("Flag is set!")

Runtime errors¶

If a program is syntactically correct – that is, free of syntax errors – it will be run by the Python interpreter. However, the program may exit unexpectedly during execution if it encounters a runtime error – a problem which was not detected when the program was parsed, but is only revealed when a particular line is executed. When a program comes to a halt because of a runtime error, we say that it has crashed.

Consider the English instruction flap your arms and fly to Australia. While the instruction is structurally correct and you can understand its meaning perfectly, it is impossible for you to follow it.

Some examples of Python runtime errors:

  • division by zero
  • performing an operation on incompatible types
  • using an identifier which has not been defined
  • accessing a list element, dictionary value or object attribute which doesn’t exist
  • trying to access a file which doesn’t exist

Runtime errors often creep in if you don’t consider all possible values that a variable could contain, especially when you are processing user input. You should always try to add checks to your code to make sure that it can deal with bad input and edge cases gracefully. We will look at this in more detail in the chapter about exception handling.

Logical errors¶

Logical errors are the most difficult to fix. They occur when the program runs without crashing, but produces an incorrect result. The error is caused by a mistake in the program’s logic. You won’t get an error message, because no syntax or runtime error has occurred. You will have to find the problem on your own by reviewing all the relevant parts of your code – although some tools can flag suspicious code which looks like it could cause unexpected behaviour.

Sometimes there can be absolutely nothing wrong with your Python implementation of an algorithm – the algorithm itself can be incorrect. However, more frequently these kinds of errors are caused by programmer carelessness. Here are some examples of mistakes which lead to logical errors:

  • using the wrong variable name
  • indenting a block to the wrong level
  • using integer division instead of floating-point division
  • getting operator precedence wrong
  • making a mistake in a boolean expression
  • off-by-one, and other numerical errors

If you misspell an identifier name, you may get a runtime error or a logical error, depending on whether the misspelled name is defined.

A common source of variable name mix-ups and incorrect indentation is frequent copying and pasting of large blocks of code. If you have many duplicate lines with minor differences, it’s very easy to miss a necessary change when you are editing your pasted lines. You should always try to factor out excessive duplication using functions and loops – we will look at this in more detail later.

Exercise 1¶

  1. Find all the syntax errors in the code snippet above, and explain why they are errors.

  2. Find potential sources of runtime errors in this code snippet:

    dividend = float(input("Please enter the dividend: "))
    divisor = float(input("Please enter the divisor: "))
    quotient = dividend / divisor
    quotient_rounded = math.round(quotient)
    
  3. Find potential sources of runtime errors in this code snippet:

    for x in range(a, b):
        print("(%f, %f, %f)" % my_list[x])
    
  4. Find potential sources of logic errors in this code snippet:

    product = 0
    for i in range(10):
        product *= i
    
    sum_squares = 0
    for i in range(10):
        i_sq = i**2
    sum_squares += i_sq
    
    nums = 0
    for num in range(10):
        num += num
    

Handling exceptions¶

Until now, the programs that we have written have generally ignored the fact that things can go wrong. We have have tried to prevent runtime errors by checking data which may be incorrect before we used it, but we haven’t yet seen how we can handle errors when they do occur – our programs so far have just crashed suddenly whenever they have encountered one.

There are some situations in which runtime errors are likely to occur. Whenever we try to read a file or get input from a user, there is a chance that something unexpected will happen – the file may have been moved or deleted, and the user may enter data which is not in the right format. Good programmers should add safeguards to their programs so that common situations like this can be handled gracefully – a program which crashes whenever it encounters an easily foreseeable problem is not very pleasant to use. Most users expect programs to be robust enough to recover from these kinds of setbacks.

If we know that a particular section of our program is likely to cause an error, we can tell Python what to do if it does happen. Instead of letting the error crash our program we can intercept it, do something about it, and allow the program to continue.

All the runtime (and syntax) errors that we have encountered are called exceptions in Python – Python uses them to indicate that something exceptional has occurred, and that your program cannot continue unless it is handled. All exceptions are subclasses of the Exception class – we will learn more about classes, and how to write your own exception types, in later chapters.

The try and except statements¶

To handle possible exceptions, we use a try-except block:

try:
    age = int(input("Please enter your age: "))
    print("I see that you are %d years old." % age)
except ValueError:
    print("Hey, that wasn't a number!")

Python will try to process all the statements inside the try block. If a ValueError occurs at any point as it is executing them, the flow of control will immediately pass to the except block, and any remaining statements in the try block will be skipped.

In this example, we know that the error is likely to occur when we try to convert the user’s input to an integer. If the input string is not a number, this line will trigger a ValueError – that is why we specified it as the type of error that we are going to handle.

We could have specified a more general type of error – or even left the type out entirely, which would have caused the except clause to match any kind of exception – but that would have been a bad idea. What if we got a completely different error that we hadn’t predicted? It would be handled as well, and we wouldn’t even notice that anything unusual was going wrong. We may also want to react in different ways to different kinds of errors. We should always try pick specific rather than general error types for our except clauses.

It is possible for one except clause to handle more than one kind of error: we can provide a tuple of exception types instead of a single type:

try:
    dividend = int(input("Please enter the dividend: "))
    divisor = int(input("Please enter the divisor: "))
    print("%d / %d = %f" % (dividend, divisor, dividend/divisor))
except(ValueError, ZeroDivisionError):
    print("Oops, something went wrong!")

A try-except block can also have multiple except clauses. If an exception occurs, Python will check each except clause from the top down to see if the exception type matches. If none of the except clauses match, the exception will be considered unhandled, and your program will crash:

try:
    dividend = int(input("Please enter the dividend: "))
    divisor = int(input("Please enter the divisor: "))
    print("%d / %d = %f" % (dividend, divisor, dividend/divisor))
except ValueError:
    print("The divisor and dividend have to be numbers!")
except ZeroDivisionError:
    print("The dividend may not be zero!")

Note that in the example above if a ValueError occurs we won’t know whether it was caused by the dividend or the divisor not being an integer – either one of the input lines could cause that error. If we want to give the user more specific feedback about which input was wrong, we will have to wrap each input line in a separate try-except block:

try:
    dividend = int(input("Please enter the dividend: "))
except ValueError:
    print("The dividend has to be a number!")

try:
    divisor = int(input("Please enter the divisor: "))
except ValueError:
    print("The divisor has to be a number!")

try:
    print("%d / %d = %f" % (dividend, divisor, dividend/divisor))
except ZeroDivisionError:
    print("The dividend may not be zero!")

In general, it is a better idea to use exception handlers to protect small blocks of code against specific errors than to wrap large blocks of code and write vague, generic error recovery code. It may sometimes seem inefficient and verbose to write many small try-except statements instead of a single catch-all statement, but we can mitigate this to some extent by making effective use of loops and functions to reduce the amount of code duplication.

How an exception is handled¶

When an exception occurs, the normal flow of execution is interrupted. Python checks to see if the line of code which caused the exception is inside a try block. If it is, it checks to see if any of the except blocks associated with the try block can handle that type of exception. If an appropriate handler is found, the exception is handled, and the program continues from the next statement after the end of that try-except.

If there is no such handler, or if the line of code was not in a try block, Python will go up one level of scope: if the line of code which caused the exception was inside a function, that function will exit immediately, and the line which called the function will be treated as if it had thrown the exception. Python will check if that line is inside a try block, and so on. When a function is called, it is placed on Python’s stack, which we will discuss in the chapter about functions. Python traverses this stack when it tries to handle an exception.

If an exception is thrown by a line which is in the main body of your program, not inside a function, the program will terminate. When the exception message is printed, you should also see a traceback – a list which shows the path the exception has taken, all the way back to the original line which caused the error.

Error checks vs exception handling¶

Exception handling gives us an alternative way to deal with error-prone situations in our code. Instead of performing more checks before we do something to make sure that an error will not occur, we just try to do it – and if an error does occur we handle it. This can allow us to write simpler and more readable code. Let’s look at a more complicated input example – one in which we want to keep asking the user for input until the input is correct. We will try to write this example using the two different approaches:

# with checks

n = None
while n is None:
    s = input("Please enter an integer: ")
    if s.lstrip('-').isdigit():
        n = int(s)
    else:
        print("%s is not an integer." % s)

# with exception handling

n = None
while n is None:
    try:
        s = input("Please enter an integer: ")
        n = int(s)
    except ValueError:
        print("%s is not an integer." % s)

In the first code snippet, we have to write quite a convoluted check to test whether the user’s input is an integer – first we strip off a minus sign if it exists, and then we check if the rest of the string consists only of digits. But there’s a very simple criterion which is also what we really want to know: will this string cause a ValueError if we try to convert it to an integer? In the second snippet we can in effect check for exactly the right condition instead of trying to replicate it ourselves – something which isn’t always easy to do. For example, we could easily have forgotten that integers can be negative, and written the check in the first snippet incorrectly.

Here are a few other advantages of exception handling:

  • It separates normal code from code that handles errors.
  • Exceptions can easily be passed along functions in the stack until they reach a function which knows how to handle them. The intermediate functions don’t need to have any error-handling code.
  • Exceptions come with lots of useful error information built in – for example, they can print a traceback which helps us to see exactly where the error occurred.

The else and finally statements¶

There are two other clauses that we can add to a try-except block: else and finally. else will be executed only if the try clause doesn’t raise an exception:

try:
    age = int(input("Please enter your age: "))
except ValueError:
    print("Hey, that wasn't a number!")
else:
    print("I see that you are %d years old." % age)

We want to print a message about the user’s age only if the integer conversion succeeds. In the first exception handler example, we put this print statement directly after the conversion inside the try block. In both cases, the statement will only be executed if the conversion statement doesn’t raise an exception, but putting it in the else block is better practice – it means that the only code inside the try block is the single line that is the potential source of the error that we want to handle.

When we edit this program in the future, we may introduce additional statements that should also be executed if the age input is successfully converted. Some of these statements may also potentially raise a ValueError. If we don’t notice this, and put them inside the try clause, the except clause will also handle these errors if they occur. This is likely to cause some odd and unexpected behaviour. By putting all this extra code in the else clause instead, we avoid taking this risk.

The finally clause will be executed at the end of the try-except block no matter what – if there is no exception, if an exception is raised and handled, if an exception is raised and not handled, and even if we exit the block using break, continue or return. We can use the finally clause for cleanup code that we always want to be executed:

try:
    age = int(input("Please enter your age: "))
except ValueError:
    print("Hey, that wasn't a number!")
else:
    print("I see that you are %d years old." % age)
finally:
    print("It was really nice talking to you.  Goodbye!")

Exercise 2¶

  1. Extend the program in exercise 7 of the loop control statements chapter to include exception handling. Whenever the user enters input of the incorrect type, keep prompting the user for the same value until it is entered correctly. Give the user sensible feedback.

  2. Add a try-except statement to the body of this function which handles a possible IndexError, which could occur if the index provided exceeds the length of the list. Print an error message if this happens:

    def print_list_element(thelist, index):
        print(thelist[index])
    
  3. This function adds an element to a list inside a dict of lists. Rewrite it to use a try-except statement which handles a possible KeyError if the list with the name provided doesn’t exist in the dictionary yet, instead of checking beforehand whether it does. Include else and finally clauses in your try-except block:

    def add_to_list_in_dict(thedict, listname, element):
        if listname in thedict:
            l = thedict[listname]
            print("%s already has %d elements." % (listname, len(l)))
        else:
            thedict[listname] = []
            print("Created %s." % listname)
    
        thedict[listname].append(element)
    
        print("Added %s to %s." % (element, listname))
    

The with statement¶

Using the exception object¶

Python’s exception objects contain more information than just the error type. They also come with some kind of message – we have already seen some of these messages displayed when our programs have crashed. Often these messages aren’t very user-friendly – if we want to report an error to the user we usually need to write a more descriptive message which explains how the error is related to what the user did. For example, if the error was caused by incorrect input, it is helpful to tell the user which of the input values was incorrect.

Sometimes the exception message contains useful information which we want to display to the user. In order to access the message, we need to be able to access the exception object. We can assign the object to a variable that we can use inside the except clause like this:

try:
    age = int(input("Please enter your age: "))
except ValueError as err:
    print(err)

err is not a string, but Python knows how to convert it into one – the string representation of an exception is the message, which is exactly what we want. We can also combine the exception message with our own message:

try:
    age = int(input("Please enter your age: "))
except ValueError as err:
    print("You entered incorrect age input: %s" % err)

Note that inserting a variable into a formatted string using %s also converts the variable to a string.

Raising exceptions¶

We can raise exceptions ourselves using the raise statement:

try:
    age = int(input("Please enter your age: "))
    if age < 0:
        raise ValueError("%d is not a valid age. Age must be positive or zero.")
except ValueError as err:
    print("You entered incorrect age input: %s" % err)
else:
    print("I see that you are %d years old." % age)

We can raise our own ValueError if the age input is a valid integer, but it’s negative. When we do this, it has exactly the same effect as any other exception – the flow of control will immediately exit the try clause at this point and pass to the except clause. This except clause can match our exception as well, since it is also a ValueError.

We picked ValueError as our exception type because it’s the most appropriate for this kind of error. There’s nothing stopping us from using a completely inappropriate exception class here, but we should try to be consistent. Here are a few common exception types which we are likely to raise in our own code:

  • TypeError: this is an error which indicates that a variable has the wrong type for some operation. We might raise it in a function if a parameter is not of a type that we know how to handle.
  • ValueError: this error is used to indicate that a variable has the right type but the wrong value. For example, we used it when age was an integer, but the wrong kind of integer.
  • NotImplementedError: we will see in the next chapter how we use this exception to indicate that a class’s method has to be implemented in a child class.

We can also write our own custom exception classes which are based on existing exception classes – we will see some examples of this in a later chapter.

Something we may want to do is raise an exception that we have just intercepted – perhaps because we want to handle it partially in the current function, but also want to respond to it in the code which called the function:

try:
    age = int(input("Please enter your age: "))
except ValueError as err:
    print("You entered incorrect age input: %s" % err)
    raise err

Exercise 3¶

  1. Rewrite the program from the first question of exercise 2 so that it prints the text of Python’s original exception inside the except clause instead of a custom message.
  2. Rewrite the program from the second question of exercise 2 so that the exception which is caught in the except clause is re-raised after the error message is printed.

Debugging programs¶

Syntax errors are usually quite straightforward to debug: the error message shows us the line in the file where the error is, and it should be easy to find it and fix it.

Runtime errors can be a little more difficult to debug: the error message and the traceback can tell us exactly where the error occurred, but that doesn’t necessarily tell us what the problem is. Sometimes they are caused by something obvious, like an incorrect identifier name, but sometimes they are triggered by a particular state of the program – it’s not always clear which of many variables has an unexpected value.

Logical errors are the most difficult to fix because they don’t cause any errors that can be traced to a particular line in the code. All that we know is that the code is not behaving as it should be – sometimes tracking down the area of the code which is causing the incorrect behaviour can take a long time.

It is important to test your code to make sure that it behaves the way that you expect. A quick and simple way of testing that a function is doing the right thing, for example, is to insert a print statement after every line which outputs the intermediate results which were calculated on that line. Most programmers intuitively do this as they are writing a function, or perhaps if they need to figure out why it isn’t doing the right thing:

def hypotenuse(x, y):
    print("x is %f and y is %f" % (x, y))
    x_2 = x**2
    print(x_2)
    y_2 = y**2
    print(y_2)
    z_2 = x_2 + y_2
    print(z_2)
    z = math.sqrt(z_2)
    print(z)
    return z

This is a quick and easy thing to do, and even experienced programmers are guilty of doing it every now and then, but this approach has several disadvantages:

  • As soon as the function is working, we are likely to delete all the print statements, because we don’t want our program to print all this debugging information all the time. The problem is that code often changes – the next time we want to test this function we will have to add the print statements all over again.

  • To avoid rewriting the print statements if we happen to need them again, we may be tempted to comment them out instead of deleting them – leaving them to clutter up our code, and possibly become so out of sync that they end up being completely useless anyway.

  • To print out all these intermediate values, we had to spread out the formula inside the function over many lines. Sometimes it is useful to break up a calculation into several steps, if it is very long and putting it all on one line makes it hard to read, but sometimes it just makes our code unnecessarily verbose. Here is what the function above would normally look like:

    def hypotenuse(x, y):
        return math.sqrt(x**2 + y**2)
    

How can we do this better? If we want to inspect the values of variables at various steps of a program’s execution, we can use a tool like pdb. If we want our program to print out informative messages, possibly to a file, and we want to be able to control the level of detail at runtime without having to change anything in the code, we can use logging.

Most importantly, to check that our code is working correctly now and will keep working correctly, we should write a permanent suite of tests which we can run on our code regularly. We will discuss testing in more detail in a later chapter.

Logging¶

Sometimes it is valuable for a program to output messages to a console or a file as it runs. These messages can be used as a record of the program’s execution, and help us to find errors. Sometimes a bug occurs intermittently, and we don’t know what triggers it – if we only add debugging output to our program when we want to begin an active search for the bug, we may be unable to reproduce it. If our program logs messages to a file all the time, however, we may find that some helpful information has been recorded when we check the log after the bug has occurred.

Some kinds of messages are more important than others – errors are noteworthy events which should almost always be logged. Messages which record that an operation has been completed successfully may sometimes be useful, but are not as important as errors. Detailed messages which debug every step of a calculation can be interesting if we are trying to debug the calculation, but if they were printed all the time they would fill the console with noise (or make our log file really, really big).

We can use Python’s logging module to add logging to our program in an easy and consistent way. Logging statements are almost like print statements, but whenever we log a message we specify a level for the message. When we run our program, we set a desired log level for the program. Only messages which have a level greater than or equal to the level which we have set will appear in the log. This means that we can temporarily switch on detailed logging and switch it off again just by changing the log level in one place.

There is a consistent set of logging level names which most languages use. In order, from the highest value (most severe) to the lowest value (least severe), they are:

  • CRITICAL – for very serious errors
  • ERROR – for less serious errors
  • WARNING – for warnings
  • INFO – for important informative messages
  • DEBUG – for detailed debugging messages

These names are used for integer constants defined in the logging module. The module also provides methods which we can use to log messages. By default these messages are printed to the console, and the default log level is WARNING. We can configure the module to customise its behaviour – for example, we can write the messages to a file instead, raise or lower the log level and change the message format. Here is a simple logging example:

import logging

# log messages to a file, ignoring anything less severe than ERROR
logging.basicConfig(filename='myprogram.log', level=logging.ERROR)

# these messages should appear in our file
logging.error("The washing machine is leaking!")
logging.critical("The house is on fire!")

# but these ones won't
logging.warning("We're almost out of milk.")
logging.info("It's sunny today.")
logging.debug("I had eggs for breakfast.")

There’s also a special exception method which is used for logging exceptions. The level used for these messages is ERROR, but additional information about the exception is added to them. This method is intended to be used inside exception handlers instead of error:

try:
    age = int(input("How old are you? "))
except ValueError as err:
    logging.exception(err)

If we have a large project, we may want to set up a more complicated system for logging – perhaps we want to format certain messages differently, log different messages to different files, or log to multiple locations at the same time. The logging module also provides us with logger and handler objects for this purpose. We can use multiple loggers to create our messages, customising each one independently. Different handlers are associated with different logging locations. We can connect up our loggers and handlers in any way we like – one logger can use many handlers, and multiple loggers can use the same handler.

Exercise 4¶

  1. Write logging configuration for a program which logs to a file called log.txt and discards all logs less important than INFO.
  2. Rewrite the second program from exercise 2 so that it uses this logging configuration instead of printing messages to the console (except for the first print statement, which is the purpose of the function).
  3. Do the same with the third program from exercise 2.

Answers to exercises¶

Answer to exercise 1¶

  1. There are five syntax errors:

    1. Missing def keyword in function definition
    2. else clause without an if
    3. Missing colon after if condition
    4. Spelling mistake (“esle”)
    5. The if block is empty because the print statement is not indented correctly
    1. The values entered by the user may not be valid integers or floating-point numbers.
    2. The user may enter zero for the divisor.
    3. If the math library hasn’t been imported, math.round is undefined.
    1. a, b and my_list need to be defined before this snippet.
    2. The attempt to access the list element with index x may fail during one of the loop iterations if the range from a to b exceeds the size of my_list.
    3. The string formatting operation inside the print statement expects my_list[x] to be a tuple with three numbers. If it has too many or too few elements, or isn’t a tuple at all, the attempt to format the string will fail.
    1. If you are accumulating a number total by multiplication, not addition, you need to initialise the total to 1, not 0, otherwise the product will always be zero!
    2. The line which adds i_sq to sum_squares is not aligned correctly, and will only add the last value of i_sq after the loop has concluded.
    3. The wrong variable is used: at each loop iteration the current number in the range is added to itself and nums remains unchanged.

Answer to exercise 2¶

  1. Here is an example program:

    person = {}
    
    properties = [
        ("name", str),
        ("surname", str),
        ("age", int),
        ("height", float),
        ("weight", float),
    ]
    
    for property, p_type in properties:
        valid_value = None
    
        while valid_value is None:
            try:
                value = input("Please enter your %s: " % property)
                valid_value = p_type(value)
            except ValueError:
                print("Could not convert %s '%s' to type %s. Please try again." % (property, value, p_type.__name__))
    
        person[property] = valid_value
    
  2. Here is an example program:

    def print_list_element(thelist, index):
        try:
            print(thelist[index])
        except IndexError:
            print("The list has no element at index %d." % index)
    
  3. Here is an example program:

    def add_to_list_in_dict(thedict, listname, element):
        try:
            l = thedict[listname]
        except KeyError:
            thedict[listname] = []
            print("Created %s." % listname)
        else:
            print("%s already has %d elements." % (listname, len(l)))
        finally:
            thedict[listname].append(element)
            print("Added %s to %s." % (element, listname))
    

Answer to exercise 3¶

  1. Here is an example program:

    person = {}
    
    properties = [
        ("name", str),
        ("surname", str),
        ("age", int),
        ("height", float),
        ("weight", float),
    ]
    
    for property, p_type in properties:
        valid_value = None
    
        while valid_value is None:
            try:
                value = input("Please enter your %s: " % property)
                valid_value = p_type(value)
            except ValueError as ve:
                print(ve)
    
        person[property] = valid_value
    
  2. Here is an example program:

    def print_list_element(thelist, index):
        try:
            print(thelist[index])
        except IndexError as ie:
            print("The list has no element at index %d." % index)
            raise ie
    

Answer to exercise 4¶

  1. Here is an example of the logging configuration:

    import logging
    logging.basicConfig(filename='log.txt', level=logging.INFO)
    
  2. Here is an example program:

    def print_list_element(thelist, index):
        try:
            print(thelist[index])
        except IndexError:
            logging.error("The list has no element at index %d." % index)
    
  3. Here is an example program:

    def add_to_list_in_dict(thedict, listname, element):
        try:
            l = thedict[listname]
        except KeyError:
            thedict[listname] = []
            logging.info("Created %s." % listname)
        else:
            logging.info("%s already has %d elements." % (listname, len(l)))
        finally:
            thedict[listname].append(element)
            logging.info("Added %s to %s." % (element, listname))
    

In python there are three types of errors; syntax errors, logic errors and exceptions.

Syntax errors[edit | edit source]

Syntax errors are the most basic type of error. They arise when the Python parser is unable to understand a line of code. Syntax errors are almost always fatal, i.e. there is almost never a way to successfully execute a piece of code containing syntax errors.
Some syntax errors can be caught and handled, like eval(«»), but these are rare.

In IDLE, it will highlight where the syntax error is. Most syntax errors are typos, incorrect indentation, or incorrect arguments. If you get this error, try looking at your code for any of these.

Logic errors[edit | edit source]

These are the most difficult type of error to find, because they will give unpredictable results and may crash your program.  A lot of different things can happen if you have a logic error. However these are very easy to fix as you can use a debugger, which will run through the program and fix any problems.

A simple example of a logic error can be showcased below, the while loop will compile and run however, the loop will never finish and may crash Python:

#Counting Sheep
#Goal: Print number of sheep up until 101.
sheep_count=1
while sheep_count<100:
    print("%i Sheep"%sheep_count)

Logic errors are only erroneous in the perspective of the programming goal one might have; in many cases Python is working as it was intended, just not as the user intended. The above while loop is functioning correctly as Python is intended to, but the exit the condition the user needs is missing.

Exceptions[edit | edit source]

Exceptions arise when the python parser knows what to do with a piece of code but is unable to perform the action. An example would be trying to access the internet with python without an internet connection; the python interpreter knows what to do with that command but is unable to perform it.

Dealing with exceptions[edit | edit source]

Unlike syntax errors, exceptions are not always fatal. Exceptions can be handled with the use of a try statement.

Consider the following code to display the HTML of the website ‘example.com’. When the execution of the program reaches the try statement it will attempt to perform the indented code following, if for some reason there is an error (the computer is not connected to the internet or something) the python interpreter will jump to the indented code below the ‘except:’ command.

import urllib2
url = 'http://www.example.com'
try:
    req = urllib2.Request(url)
    response = urllib2.urlopen(req)
    the_page = response.read()
    print(the_page)
except:
    print("We have a problem.")

Another way to handle an error is to except a specific error.

try:
    age = int(raw_input("Enter your age: "))
    print("You must be {0} years old.".format(age))
except ValueError:
    print("Your age must be numeric.")

If the user enters a numeric value as his/her age, the output should look like this:

Enter your age: 5
Your age must be 5 years old.

However, if the user enters a non-numeric value as his/her age, a ValueError is thrown when trying to execute the int() method on a non-numeric string, and the code under the except clause is executed:

Enter your age: five
Your age must be numeric.

You can also use a try block with a while loop to validate input:

valid = False
while valid == False:
    try:
        age = int(raw_input("Enter your age: "))
        valid = True     # This statement will only execute if the above statement executes without error.
        print("You must be {0} years old.".format(age))
    except ValueError:
        print("Your age must be numeric.")

The program will prompt you for your age until you enter a valid age:

Enter your age: five
Your age must be numeric.
Enter your age: abc10
Your age must be numeric.
Enter your age: 15
You must be 15 years old.

In certain other cases, it might be necessary to get more information about the exception and deal with it appropriately. In such situations the except as construct can be used.

f=raw_input("enter the name of the file:")
l=raw_input("enter the name of the link:")
try:
    os.symlink(f,l)
except OSError as e:
    print("an error occurred linking %s to %s: %sn error no %d"%(f,l,e.args[1],e.args[0]))
enter the name of the file:file1.txt
enter the name of the link:AlreadyExists.txt
an error occurred linking file1.txt to AlreadyExists.txt: File exists
 error no 17

enter the name of the file:file1.txt
enter the name of the link:/Cant/Write/Here/file1.txt
an error occurred linking file1.txt to /Cant/Write/Here/file1.txt: Permission denied
 error no 13

In this article, you will learn error and exception handling in Python.

By the end of the article, you’ll know:

  • How to handle exceptions using the try, except, and finally statements
  • How to create a custom exception
  • How to raise an exceptions
  • How to use built-in exception effectively to build robust Python programs
Python Exceptions
Python Exceptions

Table of contents

  • What are Exceptions?
    • Why use Exception
  • What are Errors?
    • Syntax error
    • Logical errors (Exception)
  • Built-in Exceptions
  • The try and except Block to Handling Exceptions
    • Catching Specific Exceptions
    • Handle multiple exceptions with a single except clause
  • Using try with finally
  • Using try with else clause
  • Raising an Exceptions
  • Exception Chaining
  • Custom and User-defined Exceptions
    • Customizing Exception Classes
  • Exception Lifecycle
  • Warnings

What are Exceptions?

An exception is an event that occurs during the execution of programs that disrupt the normal flow of execution (e.g., KeyError Raised when a key is not found in a dictionary.) An exception is a Python object that represents an error..

In Python, an exception is an object derives from the BaseException class that contains information about an error event that occurred within a method. Exception object contains:

  • Error type (exception name)
  • The state of the program when the error occurred
  • An error message describes the error event.

Exception are useful to indicate different types of possible failure condition.

For example, bellow are the few standard exceptions

  • FileNotFoundException
  • ImportError
  • RuntimeError
  • NameError
  • TypeError

In Python, we can throw an exception in the try block and catch it in except block.

Why use Exception

  • Standardized error handling: Using built-in exceptions or creating a custom exception with a more precise name and description, you can adequately define the error event, which helps you debug the error event.
  • Cleaner code: Exceptions separate the error-handling code from regular code, which helps us to maintain large code easily.
  • Robust application: With the help of exceptions, we can develop a solid application, which can handle error event efficiently
  • Exceptions propagation: By default, the exception propagates the call stack if you don’t catch it. For example, if any error event occurred in a nested function, you do not have to explicitly catch-and-forward it; automatically, it gets forwarded to the calling function where you can handle it.
  • Different error types: Either you can use built-in exception or create your custom exception and group them by their generalized parent class, or Differentiate errors by their actual class

What are Errors?

On the other hand, An error is an action that is incorrect or inaccurate. For example, syntax error. Due to which the program fails to execute.

The errors can be broadly classified into two types:

  1. Syntax errors
  2. Logical errors

Syntax error

The syntax error occurs when we are not following the proper structure or syntax of the language. A syntax error is also known as a parsing error.

When Python parses the program and finds an incorrect statement it is known as a syntax error. When the parser found a syntax error it exits with an error message without running anything.

Common Python Syntax errors:

  • Incorrect indentation
  • Missing colon, comma, or brackets
  • Putting keywords in the wrong place.

Example

print("Welcome to PYnative")
    print("Learn Python with us..")

Output

print("Learn Python with us..")
    ^
IndentationError: unexpected indent

Logical errors (Exception)

Even if a statement or expression is syntactically correct, the error that occurs at the runtime is known as a Logical error or Exception. In other words, Errors detected during execution are called exceptions.

Common Python Logical errors:

  • Indenting a block to the wrong level
  • using the wrong variable name
  • making a mistake in a boolean expression

Example

a = 10
b = 20
print("Addition:", a + c)

Output

print("Addition:", a + c)
NameError: name 'c' is not defined

Built-in Exceptions

The below table shows different built-in exceptions.

Python automatically generates many exceptions and errors. Runtime exceptions, generally a result of programming errors, such as:

  • Reading a file that is not present
  • Trying to read data outside the available index of a list
  • Dividing an integer value by zero
Exception Description
AssertionError Raised when an assert statement fails.
AttributeError Raised when attribute assignment or reference fails.
EOFError Raised when the input() function hits the end-of-file condition.
FloatingPointError Raised when a floating-point operation fails.
GeneratorExit Raise when a generator’s close() method is called.
ImportError Raised when the imported module is not found.
IndexError Raised when the index of a sequence is out of range.
KeyError Raised when a key is not found in a dictionary.
KeyboardInterrupt Raised when the user hits the interrupt key (Ctrl+C or Delete)
MemoryError Raised when an operation runs out of memory.
NameError Raised when a variable is not found in the local or global scope.
OSError Raised when system operation causes system related error.
ReferenceError Raised when a weak reference proxy is used to access a garbage collected referent.
Python Built-in Exceptions

Example: The FilenotfoundError is raised when a file in not present on the disk

fp = open("test.txt", "r")
if fp:
    print("file is opened successfully")

Output:

FileNotFoundError: [Errno 2] No such file or directory: 'test.txt'

The try and except Block to Handling Exceptions

When an exception occurs, Python stops the program execution and generates an exception message. It is highly recommended to handle exceptions. The doubtful code that may raise an exception is called risky code.

To handle exceptions we need to use try and except block. Define risky code that can raise an exception inside the try block and corresponding handling code inside the except block.

Syntax

try :
    # statements in try block
except :
    # executed when exception occured in try block
try-except block
try-except block

The try block is for risky code that can raise an exception and the except block to handle error raised in a try block. For example, if we divide any number by zero, try block will throw ZeroDivisionError, so we should handle that exception in the except block.

When we do not use try…except block in the program, the program terminates abnormally, or it will be nongraceful termination of the program.

Now let’s see the example when we do not use try…except block for handling exceptions.

Example:

a = 10
b = 0
c = a / b
print("a/b = %d" % c)

Output

Traceback (most recent call last):
  File "E:/demos/exception.py", line 3, in <module>
    c = a / b
ZeroDivisionError: division by zero

We can see in the above code when we are divided by 0; Python throws an exception as ZeroDivisionError and the program terminated abnormally.

We can handle the above exception using the try…except block. See the following code.

Example

try:
    a = 10
    b = 0
    c = a/b
    print("The answer of a divide by b:", c)
except:
    print("Can't divide with zero. Provide different number")

Output

Can't divide with zero. Provide different number

Catching Specific Exceptions

We can also catch a specific exception. In the above example, we did not mention any specific exception in the except block. Catch all the exceptions and handle every exception is not good programming practice.

It is good practice to specify an exact exception that the except clause should catch. For example, to catch an exception that occurs when the user enters a non-numerical value instead of a number, we can catch only the built-in ValueError exception that will handle such an event properly.

We can specify which exception except block should catch or handle. A try block can be followed by multiple numbers of except blocks to handle the different exceptions. But only one exception will be executed when an exception occurs.

Example

In this example, we will ask the user for the denominator value. If the user enters a number, the program will evaluate and produce the result.

If the user enters a non-numeric value then, the try block will throw a ValueError exception, and we can catch that using a first catch block ‘except ValueError’ by printing the message ‘Entered value is wrong’.

And suppose the user enters the denominator as zero. In that case, the try block will throw a ZeroDivisionError, and we can catch that using a second catch block by printing the message ‘Can’t divide by zero’.

try:
    a = int(input("Enter value of a:"))
    b = int(input("Enter value of b:"))
    c = a/b
    print("The answer of a divide by b:", c)
except ValueError:
    print("Entered value is wrong")
except ZeroDivisionError:
    print("Can't divide by zero")

Output 1:

Enter value of a:Ten
Entered value is wrong

Output 2:

Enter value of a:10
Enter value of b:0
Can't divide by zero

Output 3:

Enter value of a:10
Enter value of b:2
The answer of a divide by b: 5.0

Handle multiple exceptions with a single except clause

We can also handle multiple exceptions with a single except clause. For that, we can use an tuple of values to specify multiple exceptions in an except clause.

Example

Let’s see how to specifiy two exceptions in the single except clause.

try:
    a = int(input("Enter value of a:"))
    b = int(input("Enter value of b:"))
    c = a / b
    print("The answer of a divide by b:", c)
except(ValueError, ZeroDivisionError):
    print("Please enter a valid value")

Using try with finally

Python provides the finally block, which is used with the try block statement. The finally block is used to write a block of code that must execute, whether the try block raises an error or not.

Mainly, the finally block is used to release the external resource. This block provides a guarantee of execution.

try-except-finally block
try-except-finally block

Clean-up actions using finally

Sometimes we want to execute some action at any cost, even if an error occurred in a program. In Python, we can perform such actions using a finally statement with a try and except statement.

The block of code written in the finally block will always execute even there is an exception in the try and except block.

If an exception is not handled by except clause, then finally block executes first, then the exception is thrown. This process is known as clean-up action.

Syntax

try:    
    # block of code     
    # this may throw an exception    
finally:    
    # block of code    
    # this will always be executed 
    # after the try and any except block   

Example

try:
    a = int(input("Enter value of a:"))
    b = int(input("Enter value of b:"))
    c = a / b
    print("The answer of a divide by b:", c)

except ZeroDivisionError:
    print("Can't divide with zero")
finally:
    print("Inside a finally block")

Output 1:

Enter value of a:20
Enter value of b:5
The answer of a divide by b: 4.0
Inside a finally block

Output 2:

Enter value of a:20
Enter value of b:0
Can't divide with zero
Inside a finally block

In the above example, we can see we divide a number by 0 and get an error, and the program terminates normally. In this case, the finally block was also executed.

Using try with else clause

Sometimes we might want to run a specific block of code. In that case, we can use else block with the try-except block. The else block will be executed if and only if there are no exception is the try block. For these cases, we can use the optional else statement with the try statement.

Why to use else block with try?

Use else statemen with try block to check if try block executed without any exception or if you want to run a specific code only if an exception is not raised

try-else block

Syntax

try:    
    # block of code     
except Exception1:    
    # block of code     
else:    
    # this code executes when exceptions not occured    
  • try: The try block for risky code that can throw an exception.
  • except: The except block to handle error raised in a try block.
  • else: The else block is executed if there is no exception.

Example

try:
    a = int(input("Enter value of a:"))
    b = int(input("Enter value of b:"))
    c = a / b
    print("a/b = %d" % c)

except ZeroDivisionError:
    print("Can't divide by zero")
else:
    print("We are in else block ")

Output 1

Enter value of a: 20
Enter value of b:4
a/b = 5
We are in else block 

Output 2

Enter value of a: 20
Enter value of b:0
Can't divide by zero

Raising an Exceptions

In Python, the raise statement allows us to throw an exception. The single arguments in the raise statement show an exception to be raised. This can be either an exception object or an Exception class that is derived from the Exception class.

The raise statement is useful in situations where we need to raise an exception to the caller program. We can raise exceptions in cases such as wrong data received or any validation failure.

Follow the below steps to raise an exception:

  • Create an exception of the appropriate type. Use the existing built-in exceptions or create your won exception as per the requirement.
  • Pass the appropriate data while raising an exception.
  • Execute a raise statement, by providing the exception class.

The syntax to use the raise statement is given below.

raise Exception_class,<value>  

Example

In this example, we will throw an exception if interest rate is greater than 100.

def simple_interest(amount, year, rate):
    try:
        if rate > 100:
            raise ValueError(rate)
        interest = (amount * year * rate) / 100
        print('The Simple Interest is', interest)
        return interest
    except ValueError:
        print('interest rate is out of range', rate)

print('Case 1')
simple_interest(800, 6, 8)

print('Case 2')
simple_interest(800, 6, 800)

Output:

Case 1
The Simple Interest is 384.0

Case 2
interest rate is out of range 800

Exception Chaining

The exception chaining is available only in Python 3. The raise statements allow us as optional from statement, which enables chaining exceptions. So we can implement exception chaining in python3 by using raise…from clause to chain exception.

When exception raise, exception chaining happens automatically. The exception can be raised inside except or finally block section. We also disabled exception chaining by using from None idiom.

Example

try:
    a = int(input("Enter value of a:"))
    b = int(input("Enter value of b:"))
    c = a/b
    print("The answer of a divide by b:", c)
except ZeroDivisionError as e:
    raise ValueError("Division failed") from e

# Output: Enter value of a:10
# Enter value of b:0
# ValueError: Division failed

In the above example, we use exception chaining using raise...from clause and raise ValueError division failed.

Custom and User-defined Exceptions

Sometimes we have to define and raise exceptions explicitly to indicate that something goes wrong. Such a type of exception is called a user-defined exception or customized exception.

The user can define custom exceptions by creating a new class. This new exception class has to derive either directly or indirectly from the built-in class Exception. In Python, most of the built-in exceptions also derived from the Exception class.

class Error(Exception):
    """Base class for other exceptions"""
    pass

class ValueTooSmallError(Error):
    """Raised when the input value is small"""
    pass

class ValueTooLargeError(Error):
    """Raised when the input value is large"""
    pass

while(True):
    try:
        num = int(input("Enter any value in 10 to 50 range: "))
        if num < 10:
            raise ValueTooSmallError
        elif num > 50:
            raise ValueTooLargeError
        break
    except ValueTooSmallError:
            print("Value is below range..try again")

    except ValueTooLargeError:
            print("value out of range...try again")

print("Great! value in correct range.")

Output

Enter any value in 10 to 50 range: 5
Value is below range..try again

Enter any value in 10 to 50 range: 60
value out of range...try again

Enter any value in 10 to 50 range: 11
Great! value in correct range.

In the above example, we create two custom classes or user-defined classes with names, ValueTooSmallError and ValueTooLargeError.When the entered value is below the range, it will raise ValueTooSmallError and if the value is out of then, it will raise ValueTooLargeError.

Customizing Exception Classes

We can customize the classes by accepting arguments as per our requirements. Any custom exception class must be Extending from BaseException class or subclass of BaseException.

In the above example, we create a custom class that is inherited from the base class Exception. This class takes one argument age. When entered age is negative, it will raise NegativeAgeError.

class NegativeAgeError(Exception):

    def __init__(self, age, ):
        message = "Age should not be negative"
        self.age = age
        self.message = message

age = int(input("Enter age: "))
if age < 0:
    raise NegativeAgeError(age)
# Output:
# raise NegativeAgeError(age)
# __main__.NegativeAgeError: -9

Output:

Enter age: -28
 Traceback (most recent call last):
   File "E:/demos/exception.py", line 11, in 
     raise NegativeAgeError(age)
 main.NegativeAgeError: -28

Done

Exception Lifecycle

  • When an exception is raised, The runtime system attempts to find a handler for the exception by backtracking the ordered list of methods calls. This is known as the call stack.
  • If a handler is found (i.e., if except block is located), there are two cases in the except block; either exception is handled or possibly re-thrown.
  • If the handler is not found (the runtime backtracks to the method chain’s last calling method), the exception stack trace is printed to the standard error console, and the application stops its execution.

Example

def sum_of_list(numbers):
    return sum(numbers)

def average(sum, n):
    # ZeroDivisionError if list is empty
    return sum / n

def final_data(data):
    for item in data:
        print("Average:", average(sum_of_list(item), len(item)))

list1 = [10, 20, 30, 40, 50]
list2 = [100, 200, 300, 400, 500]
# empty list
list3 = []
lists = [list1, list2, list3]
final_data(lists)

Output

Average: 30.0
Traceback (most recent call last):
File "E:/demos/exceptions.py", line 17, in
final_data(lists)
File "E:/demos/exceptions.py", line 11, in final_data
print("Average:", average(sum_of_list(item), len(item)))
Average: 300.0
File "E:/demos/exceptions.py", line 6, in average
return sum / n
ZeroDivisionError: division by zero

The above stack trace shows the methods that are being called from main() until the method created an exception condition. It also shows line numbers.

Warnings

Several built-in exceptions represent warning categories. This categorization is helpful to be able to filter out groups of warnings.

The warning doesn’t stop the execution of a program it indicates the possible improvement

Below is the list of warning exception

Waring Class Meaning
Warning Base class for warning categories
UserWarning Base class for warnings generated by user code
DeprecationWarning Warnings about deprecated features
PendingDeprecationWarning Warnings about features that are obsolete and expected to be deprecated in the future, but are not deprecated at the moment.
SyntaxWarning Warnings about dubious syntax
RuntimeWarning Warnings about the dubious runtime behavior
FutureWarning Warnings about probable mistakes in module imports
ImportWarning Warnings about probable mistakes in module imports
UnicodeWarning Warnings related to Unicode data
BytesWarning Warnings related to bytes and bytearray.
ResourceWarning Warnings related to resource usage
Python Exception warnings

Until now error messages haven’t been more than mentioned, but if you have tried out the examples you have probably seen some. There are (at least) two distinguishable kinds of errors: syntax errors and exceptions.

8.1. Syntax Errors

Syntax errors, also known as parsing errors, are perhaps the most common kind of complaint you get while you are still learning Python:

>>> while True print('Hello world')
  File "<stdin>", line 1
    while True print('Hello world')
                   ^
SyntaxError: invalid syntax

The parser repeats the offending line and displays a little ‘arrow’ pointing at the earliest point in the line where the error was detected. The error is caused by (or at least detected at) the token preceding the arrow: in the example, the error is detected at the function print(), since a colon (':') is missing before it. File name and line number are printed so you know where to look in case the input came from a script.

8.2. Exceptions

Even if a statement or expression is syntactically correct, it may cause an error when an attempt is made to execute it. Errors detected during execution are called exceptions and are not unconditionally fatal: you will soon learn how to handle them in Python programs. Most exceptions are not handled by programs, however, and result in error messages as shown here:

>>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 
>>> 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str

The last line of the error message indicates what happened. Exceptions come in different types, and the type is printed as part of the message: the types in the example are ZeroDivisionError, NameError and TypeError. The string printed as the exception type is the name of the built-in exception that occurred. This is true for all built-in exceptions, but need not be true for user-defined exceptions (although it is a useful convention). Standard exception names are built-in identifiers (not reserved keywords).

The rest of the line provides detail based on the type of exception and what caused it.

The preceding part of the error message shows the context where the exception occurred, in the form of a stack traceback. In general it contains a stack traceback listing source lines; however, it will not display lines read from standard input.

Built-in Exceptions lists the built-in exceptions and their meanings.

8.3. Handling Exceptions

It is possible to write programs that handle selected exceptions. Look at the following example, which asks the user for input until a valid integer has been entered, but allows the user to interrupt the program (usingControl-Cor whatever the operating system supports); note that a user-generated interruption is signalled by raising the KeyboardInterrupt exception.

>>> while True:
...     try:
...         x = int(input("Please enter a number: "))
...         break
...     except ValueError:
...         print("Oops!  That was no valid number.  Try again...")
...

The try statement works as follows.

  • First, the try clause (the statement(s) between the try and except keywords) is executed.
  • If no exception occurs, the except clause is skipped and execution of the try statement is finished.
  • If an exception occurs during execution of the try clause, the rest of the clause is skipped. Then, if its type matches the exception named after the except keyword, the except clause is executed, and then execution continues after the try/except block.
  • If an exception occurs which does not match the exception named in the except clause, it is passed on to outer try statements; if no handler is found, it is an unhandled exception and execution stops with a message as shown above.

A try statement may have more than one except clause, to specify handlers for different exceptions. At most one handler will be executed. Handlers only handle exceptions that occur in the corresponding try clause, not in other handlers of the same try statement. An except clause may name multiple exceptions as a parenthesized tuple, for example:

... except (RuntimeError, TypeError, NameError):
...     pass

A class in an except clause is compatible with an exception if it is the same class or a base class thereof (but not the other way around — an except clause listing a derived class is not compatible with a base class). For example, the following code will print B, C, D in that order:

class B(Exception):
    pass

class C(B):
    pass

class D(C):
    pass

for cls in [B, C, D]:
    try:
        raise cls()
    except D:
        print("D")
    except C:
        print("C")
    except B:
        print("B")

Note that if the except clauses were reversed (with except B first), it would have printed B, B, B — the first matching except clause is triggered.

When an exception occurs, it may have associated values, also known as the exception’s arguments. The presence and types of the arguments depend on the exception type.

The except clause may specify a variable after the exception name. The variable is bound to the exception instance which typically has an args attribute that stores the arguments. For convenience, builtin exception types define __str__() to print all the arguments without explicitly accessing .args.

>>> try:
...     raise Exception('spam', 'eggs')
... except Exception as inst:
...     print(type(inst))    
...     print(inst.args)     
...     print(inst)          
...                          
...     x, y = inst.args     
...     print('x =', x)
...     print('y =', y)
...
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

The exception’s __str__() output is printed as the last part (‘detail’) of the message for unhandled exceptions.

BaseException is the common base class of all exceptions. One of its subclasses, Exception, is the base class of all the non-fatal exceptions. Exceptions which are not subclasses of Exception are not typically handled, because they are used to indicate that the program should terminate. They include SystemExit which is raised by sys.exit() and KeyboardInterrupt which is raised when a user wishes to interrupt the program.

Exception can be used as a wildcard that catches (almost) everything. However, it is good practice to be as specific as possible with the types of exceptions that we intend to handle, and to allow any unexpected exceptions to propagate on.

The most common pattern for handling Exception is to print or log the exception and then re-raise it (allowing a caller to handle the exception as well):

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error:", err)
except ValueError:
    print("Could not convert data to an integer.")
except Exception as err:
    print(f"Unexpected {err=}, {type(err)=}")
    raise

The tryexcept statement has an optional else clause, which, when present, must follow all except clauses. It is useful for code that must be executed if the try clause does not raise an exception. For example:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except OSError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

The use of the else clause is better than adding additional code to the try clause because it avoids accidentally catching an exception that wasn’t raised by the code being protected by the tryexcept statement.

Exception handlers do not handle only exceptions that occur immediately in the try clause, but also those that occur inside functions that are called (even indirectly) in the try clause. For example:

>>> def this_fails():
...     x = 1/0
...
>>> try:
...     this_fails()
... except ZeroDivisionError as err:
...     print('Handling run-time error:', err)
...
Handling run-time error: division by zero

8.4. Raising Exceptions

The raise statement allows the programmer to force a specified exception to occur. For example:

>>> raise NameError('HiThere')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: HiThere

The sole argument to raise indicates the exception to be raised. This must be either an exception instance or an exception class (a class that derives from BaseException, such as Exception or one of its subclasses). If an exception class is passed, it will be implicitly instantiated by calling its constructor with no arguments:

raise ValueError  

If you need to determine whether an exception was raised but don’t intend to handle it, a simpler form of the raise statement allows you to re-raise the exception:

>>> try:
...     raise NameError('HiThere')
... except NameError:
...     print('An exception flew by!')
...     raise
...
An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError: HiThere

8.5. Exception Chaining

If an unhandled exception occurs inside an except section, it will have the exception being handled attached to it and included in the error message:

>>> try:
...     open("database.sqlite")
... except OSError:
...     raise RuntimeError("unable to handle error")
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'database.sqlite'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: unable to handle error

To indicate that an exception is a direct consequence of another, the raise statement allows an optional from clause:

raise RuntimeError from exc

This can be useful when you are transforming exceptions. For example:

>>> def func():
...     raise ConnectionError
...
>>> try:
...     func()
... except ConnectionError as exc:
...     raise RuntimeError('Failed to open database') from exc
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in func
ConnectionError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Failed to open database

It also allows disabling automatic exception chaining using the from None idiom:

>>> try:
...     open('database.sqlite')
... except OSError:
...     raise RuntimeError from None
...
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError

For more information about chaining mechanics, see Built-in Exceptions.


© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/tutorial/errors.html


Python

3.11

  • Data Structures

    This chapter describes some things you’ve learned about already more detail, and adds new as well.

  • 5.4. Sets

    Python also includes data type for sets.

  • 8.6. User-defined Exceptions

    Programs may name their own exceptions by creating new class (see Classes for more about Python Exception classes can be defined which do anything other

  • Floating Point Arithmetic: Issues and Limitations

    Floating-point numbers are represented computer hardware as base 2 (binary) fractions.

In this tutorial, we will learn about exceptions in Python. We will cover exceptions and different types of exceptions in Python.

Video: Python Exception Handling

An exception is an unexpected event that occurs during program execution. For example,

divide_by_zero = 7 / 0

The above code causes an exception as it is not possible to divide a number by 0.

Let’s learn about Python Exceptions in detail.


Python Logical Errors (Exceptions)

Errors that occur at runtime (after passing the syntax test) are called exceptions or logical errors.

For instance, they occur when we

  • try to open a file(for reading) that does not exist (FileNotFoundError)
  • try to divide a number by zero (ZeroDivisionError)
  • try to import a module that does not exist (ImportError) and so on.

Whenever these types of runtime errors occur, Python creates an exception object.

If not handled properly, it prints a traceback to that error along with some details about why that error occurred.

Let’s look at how Python treats these errors:

divide_numbers = 7 / 0
prit(divide_numbers)

Output

Traceback (most recent call last):
 File "<string>", line 1, in <module>
ZeroDivisionError: division by zero

Here, while trying to divide 7 / 0, the program throws a system exception ZeroDivisionError


Python Built-in Exceptions

Illegal operations can raise exceptions. There are plenty of built-in exceptions in Python that are raised when corresponding errors occur.

We can view all the built-in exceptions using the built-in local() function as follows:

print(dir(locals()['__builtins__']))

Here, locals()['__builtins__'] will return a module of built-in exceptions, functions, and attributes and dir allows us to list these attributes as strings.

Some of the common built-in exceptions in Python programming along with the error that cause them are listed below:

Exception Cause of Error
AssertionError Raised when an assert statement fails.
AttributeError Raised when attribute assignment or reference fails.
EOFError Raised when the input() function hits end-of-file condition.
FloatingPointError Raised when a floating point operation fails.
GeneratorExit Raise when a generator’s close() method is called.
ImportError Raised when the imported module is not found.
IndexError Raised when the index of a sequence is out of range.
KeyError Raised when a key is not found in a dictionary.
KeyboardInterrupt Raised when the user hits the interrupt key (Ctrl+C or Delete).
MemoryError Raised when an operation runs out of memory.
NameError Raised when a variable is not found in local or global scope.
NotImplementedError Raised by abstract methods.
OSError Raised when system operation causes system related error.
OverflowError Raised when the result of an arithmetic operation is too large to be represented.
ReferenceError Raised when a weak reference proxy is used to access a garbage collected referent.
RuntimeError Raised when an error does not fall under any other category.
StopIteration Raised by next() function to indicate that there is no further item to be returned by iterator.
SyntaxError Raised by parser when syntax error is encountered.
IndentationError Raised when there is incorrect indentation.
TabError Raised when indentation consists of inconsistent tabs and spaces.
SystemError Raised when interpreter detects internal error.
SystemExit Raised by sys.exit() function.
TypeError Raised when a function or operation is applied to an object of incorrect type.
UnboundLocalError Raised when a reference is made to a local variable in a function or method, but no value has been bound to that variable.
UnicodeError Raised when a Unicode-related encoding or decoding error occurs.
UnicodeEncodeError Raised when a Unicode-related error occurs during encoding.
UnicodeDecodeError Raised when a Unicode-related error occurs during decoding.
UnicodeTranslateError Raised when a Unicode-related error occurs during translating.
ValueError Raised when a function gets an argument of correct type but improper value.
ZeroDivisionError Raised when the second operand of division or modulo operation is zero.

If required, we can also define our own exceptions in Python. To learn more about them, visit Python User-defined Exceptions.

We can handle these built-in and user-defined exceptions in Python using try, except and finally statements. To learn more about them, visit Python try, except and finally statements.


Python Error and Exception

Errors represent conditions such as compilation error, syntax error, error in the logical part of the code, library incompatibility, infinite recursion, etc.

Errors are usually beyond the control of the programmer and we should not try to handle errors.

Exceptions can be caught and handled by the program.

Now we know about exceptions, we will learn about handling exceptions in the next tutorial.

You must have got some errors on running your programs when you made some mistake in the program. It is often frustrating to get errors especially when we can’t figure out the cause of that error.

Whenever we write something wrong in our program, the interpreter throws an error when we run the program. For example, if we forget to give proper indentation to the body of a loop, then on running the program, the execution gets terminated and an error named IndentationError is thrown.

In this chapter, we will look at the types of errors we can get in Python. Errors are broadly classified into two types.

  1. Syntax Errors
  2. Exceptions (Logical Errors)

Python Syntax Errors


We get syntax errors when we don’t write the correct syntax of Python language. 

For example, we can get a syntax error when we forget to write a colon : after the condition of the if statement as shown below.

File «script.py», line 1
    if 2 < 3
           ^
SyntaxError: invalid syntax

Here the error message states that the error is in line number 1 of your program. On checking the syntax of the statement written in line number 1, you can identify you missed a colon.

Look at another example.

File «script.py», line 1
   print»Sam»)
             ^
SyntaxError: invalid syntax

We got a syntax error here because we didn’t put the opening parentheses bracket ( before “Sam” in line number 1 of the program.

You can see that we get syntax errors when we write some incorrect syntax. In these examples, our logic was correct but the syntax was wrong and so we got the syntax errors.

Python Exceptions (Logical Errors)


Exceptions are logical errors which we get when the syntax is correct and there is something wrong with our logic. Or we can say that, apart from SyntaxError, all other errors are called Exceptions.

Let’s look at some common Exceptions.

Python NameError


It is raised when a variable is not defined.

Traceback (most recent call last):
  File «script.py», line 1, in <module>
    print(num)
NameError: name ‘num’ is not defined

We got NameError because the variable num is not defined.

Python IndexError


It is raised when the index of a sequence (like list, tuple, etc) does not exist, or more technically speaking, it is raised when the index is out of range.

mylist = [1, 2, 3, 4]
print(mylist[10])

Output

Traceback (most recent call last):
  File «script.py», line 2, in <module>
    print(mylist[10])
IndexError: list index out of range

We got IndexError on printing mylist[10] because there is no index 10 of the list mylist.

Python KeyError


It is raised when the key of a dictionary is not found.

mydict = {1: 'a', 2: 'b', 3: 'c'}
print(mydict[4])

Output

Traceback (most recent call last):
  File «script.py», line 2, in <module>
    print(mydict[4])
KeyError: 4

We got KeyError on printing mydict[4] because the dictionary mydict has no key as 4.

Python IndentationError


It is raised when indentation is incorrect.

if 4 < 3:
print("4 is greater than 3")

Output

File «script.py», line 2
    print(«4 is greater than 3»)
        ^
IndentationError: expected an indented block

We got IndentationError because we didn’t give indentation to the body of the if statement. Atleast one statement should have indentation after the condition of if is checked to mark the body of if.

Python ZeroDivisionError


It is raised when some number is divided by zero.

Traceback (most recent call last):
  File «script.py», line 2, in <module>
    b = 10/a
ZeroDivisionError: division by zero

We got ZeroDivisionError because we divided a number 10 by 0.

Python ValueError


It is raised when the type of the argument passed to a function is incorrect.

Traceback (most recent call last):
  File «script.py», line 1, in <module>
    a = int(«Sam»)
ValueError: invalid literal for int() with base 10: ‘Sam’

We know that the int() function converts the value passed to it to an integer, and it can’t convert a string to an integer. Therefore, when we tried to convert the string “Sam” to integer by passing it to this function , we got ValueError.

We don’t need to remember these exceptions, but it’s always good to have a rough idea of the types of error or exceptions while debugging. To read about other built-in exceptions in Python, go to Python Built-in Exceptions.

Apart from these built-in exceptions, we can also create our own exceptions and can also handle exceptions about which we will learn in the next chapters.

To learn from simple videos, you can always look at our Python video course on CodesDope Pro. It has over 500 practice questions and over 20 projects.

One is not born a genius, one becomes a genius.

— Simone de Beauvoir


Понравилась статья? Поделить с друзьями:
  • Python logging error exception
  • Python logger error exception
  • Python last error
  • Python json response error
  • Python json loads error