DEBUGGING Notes

Download as pdf or txt
Download as pdf or txt
You are on page 1of 10

DEBUGGING

When ever we run the program we get the desired output of the program, if the program is
error free or we may end up with the error message. In this case the user is not aware of the line-
by-line result, in order to see the line-by-line output most of the IDLE offers an additional feature
called Debugger. The debugger is a feature of IDLE that executes a program one instruction at a
time, giving you a chance to inspect the values in variables while your code runs, and track how
the values change over the course of your program.
One advantage of Debugger is that it helps the users helpful to see the actual values in a program
while it runs, rather than deducing what the values might be from the source code.

Raising Exceptions
Earlier we have studied how to handle exceptions using try and except statement so that
the program recovers from exceptions. The other way to handle exception is to write our own code
using Raise keyword. Raising an exception is a way of saying, “Stop running the code in this
function and move the program execution to the except statement.”

In code, a raise statement consists of the following:


➢ The raise keyword
➢ A call to the Exception() function.
➢ A string with a helpful error message passed to the Exception() function
For Example:

Figure 1: Raise statement without try and except.

If there are no try and except statements covering the raise statement that raised the
exception, the program simply crashes and displays the exception’s error message as shown in
figure 1. Most of the times raise statement are placed inside a function and the try and except
statements are placed in the code calling the function as shown in the figure 2.
Figure 2 : boxPrint() with raise statements and try and Except statements to handle errors
Here we’ve defined a boxPrint() function that takes a character, a width, and a height, and
uses the character to make a little picture of a box with that width and height and also we use
conditional statements to ensure that the character is a single character , width and Hight are
greater than 2. The boxPrint() function is called with various arguments and whenever the
necessary condition is not met Exceptions are raised in the boxPrint() function and are handled
using try and except block which is defined outside boxPrint() function as shown is figure 2 and
the output of the above code is shown in figure 3.

Figure 3
Getting the Traceback as a String
When Python encounters an error, it gives a complete information of the error called the
traceback. The traceback includes the error message, the line number of the line that caused the
error, and the sequence of the function calls that led to the error. This sequence of calls is called
the call stack

When the above code is executed. the output will be as shown in Figure 4.

Figure 4: Overview of Traceback


From the above figure we are able to see that the error has happened on line 5, in the
bacon() function. This particular call to bacon() came from line 2, in the spam() function, which
in turn was called on line 7. In programs where functions can be called from multiple places, the
call stack can help you determine which call led to the error.
The traceback is displayed by Python whenever a raised exception goes unhandled. But
you can also obtain it as a string by calling traceback.format_exc(). This function is useful when
we need information from an exception’s traceback but also want an except statement to gracefully
handle the exception. It is necessary to import Python’s traceback module before calling
traceback.format_exc() function.
For example, instead of crashing your program right when an exception occurs, you can
write the traceback information to a log file and keep your program running. You can look at the
log file later, when you’re ready to debug your program.
Figure 5: Handling Exceptions along with traceback information.

Assertions

[Note: A sanity check or sanity test is a basic test to quickly evaluate whether a claim or the
result of a calculation can possibly be true]
An assertion is a sanity check to make sure your code isn’t doing something obviously
wrong. These sanity checks are performed by assert statements. If the sanity check fails, then an
AssertionError exception is raised.
In code, an assert statement consists of the following:
➢ The assert keyword
➢ A condition (that is, an expression that evaluates to True or False)
➢ A comma
➢ A string to display when the condition is False

Figure 6: Assertion
The value for the variable podBayDoorStatus is 'open'. In a program that uses this variable,
we might have written a lot of code under the assumption that the value is 'open'. So we add an
assertion to make sure we’re right to assume podBayDoorStatus is 'open'.

Here, we include the message 'The pod bay doors need to be "open".' so it’ll be easy to see
what’s wrong if the assertion fails. Later, say we make the obvious mistake of assigning
podBayDoorStatus another value, but don’t notice it among many lines of code. The assertion
catches this mistake and clearly tells us what’s wrong.

In plain English, an assert statement says, “I assert that this condition holds true, and if not,
there is a bug somewhere in the program.” Unlike exceptions, your code should not handle assert
statements with try and except; if an assert fails, your program should crash. By failing fast like
this, you shorten the time between the original cause of the bug and when you first notice the bug.
This will reduce the amount of code you will have to check before finding the code that’s causing
the bug.

Assertions are for programmer errors, not user errors. For errors that can be recovered
from (such as a file not being found or the user entering invalid data), raise an exception instead
of detecting it with an assert statement

Disabling Assertions

Assertions can be disabled by passing the -O option when running Python. This is good for when
you have finished writing and testing your program and don’t want it to be slowed down by
performing sanity checks (although most of the time assert statements do not cause a noticeable
speed difference). Assertions are for development, not the final product. By the time you hand off
your program to someone else to run, it should be free of bugs and not require the sanity checks.
See Appendix B for details about how to launch your probably-not-insane programs with the -O
option.

Example: Using an Assertion in a Traffic Light Simulation

************Refer Textbook for Example**********************


Logging

Logging is a great way to understand what’s happening in your program and in what order it
happening. Python’s logging module makes it easy to create a record of custom messages that you
write. These log messages will describe when the program execution has reached the logging
function call and list any variables you have specified at that point in time. On the other hand, a
missing log message indicates a part of the code was skipped and never executed.

It is necessary to import logging module to a program to display log messages on your screen as
your program runs.

When Python logs an event, it creates a LogRecord object that holds information about that
event. The logging module’s basicConfig() function lets you specify what details about the
LogRecord object you want to see and how you want those details displayed.

For Example:

In Mathematics, factorial 4 is written as 1 × 2 × 3 × 4, or 24. Factorial 7 is written as 1 × 2


× 3 × 4 × 5 × 6 × 7, or 5,040. Programmatically the mathematical form of a factorial number can
be written as shown in figure 8. The program has a bug in it and we use several log messages to
figure out what’s going wrong.

Figure 8
Here, we use the logging.debug() function when we want to print log information. This debug()
function will call basicConfig(), and a line of information will be printed. This information will be
in the format we specified in basicConfig() and will include the messages we passed to debug().
The print(factorial (5)) call is part of the original program, so the result is displayed even if logging
messages are disabled.

The output of this program is shown in figure 9:

Figure 9: Logging Information


The factorial() function is returning 0 as the factorial of 5, which isn’t right. The for loop
should be multiplying the value in total by the numbers from 1 to 5. But the log messages displayed
by logging.debug() show that the i variable is starting at 0 instead of 1. Since zero times anything
is zero, the rest of the iterations also have the wrong value for total. Logging messages provide a
trail of breadcrumbs that can help you figure out when things started to go wrong.

Changing the for i in range(n + 1): line to for i in range(1, n + 1): the bug can cleared and we get
the desired output.

The factorial(5) call in figure 10 correctly returns 120. The log messages showed what was going
on inside the loop, which led straight to the bug.It is also observed that the logging.debug() calls
printed out not just the strings passed to them but also a timestamp and the word DEBUG.
Figure 10

Logging Levels
Logging levels provide a way to categorize your log messages by importance. There are
five logging levels, described in Table 10-1 from least to most important. Messages can be logged
at each level using a different logging function.

Level Logging Description


Function

DEBUG logging.debug() The lowest level Used for small details Usually you
care about these messages only when diagnosing
problems

INFO logging.info() Used to record information on general events in your


program or confirm that things are working at their
point in the program

WARNING logging.warning() Used to indicate a potential problem that doesn’t


prevent the program from working but might do so in
the future
ERROR logging.error() Used to record an error that caused the program to fail
to do something

CRITICAL logging.critical() The highest level Used to indicate a fatal error that has
caused or is about to cause the program to stop running
entirely

Table 1: Logging Levels

The logging message is passed as a string to these functions as shown below. The logging levels
are suggestions. Ultimately, it is up to us to decide which category your log message falls into.

The benefit of logging levels is that you can change what priority of logging message you want to
see. Passing logging.DEBUG to the basicConfig() function’s level keyword argument will show
messages from all the logging levels (DEBUG being the lowest level).

Disabling Logging
After the program is debugged, if we don’t want all these log messages cluttering the
screen. The logging.disable() function disables these so that you don’t have to go into your program
and remove all the logging calls by hand. You simply pass logging.disable() a logging level, and it
will suppress all log messages at that level or lower. So if you want to disable logging entirely, just
add logging.disable(logging.CRITICAL) to your program.
IDLE’s Debugger

**********Refer the textbook***************

You might also like