Module4-Chapter1&2
Module4-Chapter1&2
Module4-Chapter1&2
Chapter-1
Organizing Files
The shutil Module
Walking a Directory Tree
Compressing Files with the zipfile Module
Project: Renaming Files with American-Style Dates to European-Style Dates
Project: Backing Up a Folder into a ZIP File
Chapter-2
Debugging
Raising Exceptions
Getting the Traceback as a String
Assertions
Logging
IDLE‟s Debugger.
Chapter -1
Organizing Files
Example:
import os
for folderName, subfolders, filenames in os.walk('C:\\delicious'):
print('The current folder is ' + folderName)
for subfolder in subfolders:
print('SUBFOLDER OF ' + folderName + ': ' + subfolder)
for filename in filenames:
print('FILE INSIDE ' + folderName + ': '+ filename)
print('')
• Since os.walk() returns lists of strings for the subfolder and filename variables, you can use these lists in
their own for loops. Replace the print() function calls with your own custom code.
• ZIP files (with the .zip file extension), which can hold the compressed contents of many
other files. Compressing a file reduces its size, which is useful when transferring it over the Internet.
• And since a ZIP file can also contain multiple files and subfolders, it’s a handy way to package several files
into one. This single file, called an archive file, can then be, say, attached to an email.
• Python programs can both create and open (or extract) ZIP files using functions in the zipfile module.
• To read the contents of a ZIP file, first you must create a ZipFile object.
• ZipFile objects are conceptually similar to the File objects you saw returned by the open() function in
the previous chapter: They are values through which the program interacts with the file.
• To create a ZipFile object, call the zipfile.ZipFile() function, passing it a string of the .zip file’s filename
• Example:
>>> import zipfile, os
>>> os.chdir(‘E:\\') # move to the folder with example.zip
>>> exampleZip = zipfile.ZipFile('example.zip')
• A ZipFile object has a namelist() method that returns a list of strings for all the files and folders
contained in the ZIP file.
>>> exampleZip.namelist()
• Strings can be passed to the getinfo() ZipFile method to return a ZipInfo object about that particular
file.
>>> spamInfo = exampleZip.getinfo('example/spam.txt')
• ZipInfo objects have their own attributes, such as file_size and compress_size in
bytes, which hold integers of the original file size and compressed file size,
respectively
>>> spamInfo.file_size
>>> spamInfo.compress_size
• The below line calculates how efficiently example.zip is compressed by dividing the
original file size by the compressed file size and prints
>>>'Compressed file is %sx smaller!' % (round(spamInfo.file_size /
spamInfo.compress_size, 2))
>>>exampleZip.close()
Program:
#! python3
# renameDates.py - Renames filenames with American MM-DD-YYYY date format
# to European DD-MM-YYYY.
import shutil, os, re
# Create a regex that matches files with the American date format.
datePattern = re.compile(r"""^(.*?) # all text before the date
((0|1)?\d)- # one or two digits for the month
((0|1|2|3)?\d)- # one or two digits for the day
((19|20)\d\d) # four digits for the year
(.*?)$ # all text after the date
""", re.VERBOSE)
# Loop over the files in the working directory.
for amerFilename in os.listdir('.'):
mo = datePattern.search(amerFilename)
# Skip files without a date.
if mo == None:
continue
# Get the different parts of the filename.
beforePart = mo.group(1)
monthPart = mo.group(2)
dayPart = mo.group(4)
yearPart = mo.group(6)
afterPart = mo.group(8)
# Form the European-style filename.
euroFilename = beforePart + dayPart + '-' + monthPart + '-' + yearPart + afterPart
# Get the full, absolute file paths.
absWorkingDir = os.path.abspath('.')
amerFilename = os.path.join(absWorkingDir, amerFilename)
euroFilename = os.path.join(absWorkingDir, euroFilename)
# Rename the files.
print('Renaming "%s" to "%s"...' % (amerFilename, euroFilename))
#shutil.move(amerFilename, euroFilename) # uncomment after testing
Program:
import zipfile, os
def backupToZip(folder):
# Backup the entire contents of "folder" into a ZIP file.
folder = os.path.abspath(folder) # make sure folder is absolute
# Figure out the filename this code should use based on
# what files already exist.
number = 1
while True:
zipFilename = os.path.basename(folder) + '_' + str(number) + '.zip'
if not os.path.exists(zipFilename):
break
number = number + 1
# TODO: Create the ZIP file.
while True:
zipFilename = os.path.basename(folder) + '_' + str(number) + '.zip'
if not os.path.exists(zipFilename):
break
number = number + 1
# Create the ZIP file.
print('Creating %s…' % (zipFilename))
backupZip = zipfile.ZipFile(zipFilename, 'w')
# TODO: Walk the entire folder tree and compress the files in each folder.
for foldername, subfolders, filenames in os.walk(folder):
print('Adding files in %s…' % (foldername))
# Add the current folder to the ZIP file.
backupZip.write(foldername)
# Add all the files in this folder to the ZIP file.
for filename in filenames:
newBase/os.path.basename(folder) + '_'
if filename.startswith(newBase) and filename.endswith('.zip'):
continue # don't backup the backup ZIP files
backupZip.write(os.path.join(foldername, filename))
backupZip.close()
print('Done.')
backupToZip('E:\example\delicious')
Chapter-2
Debugging
• The chapter covers some tools and techniques for finding the root cause of bugs in your
program to help you fix bugs faster and with less effort.
• 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.
Raising Exceptions
• Python raises an exception whenever it tries to execute invalid code.
• But you can also raise your own exceptions in your code.
• Raising an exception is a way of saying, “Stop running the code in this function and move the
program execution to the except statement.”
• Exceptions are raised with a raise 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
Example:
>>> raise Exception('This is the error message.')
• 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.
• Often it’s the code that calls the function, not the fuction itself, that knows how to handle an
expection. So you will commonly see a raise statement inside a function and the try and
except statements in the code calling the function.
• Example:
def boxPrint(symbol, width, height):
if len(symbol) != 1:
raise Exception('Symbol must be a single character string.')
if width <= 2:
raise Exception('Width must be greater than 2.')
if height <= 2:
raise Exception('Height must be greater than 2.')
print(symbol * width)
for i in range(height - 2):
print(symbol + (' ' * (width - 2)) + symbol)
print(symbol * width)
for sym, w, h in (('*', 4, 4), ('O', 20, 5), ('x', 1, 3), ('ZZ', 3, 3)):
try:
boxPrint(sym, w, h)
except Exception as err:
print('An exception happened: ' + str(err))
• 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. This box shape
is printed to the console.
• This program uses the except Exception as err form of the except statement . If an Exception
object is returned from boxPrint() , this except statement will store it in a variable named err.
The Exception object can then be converted to a string by passing it to str() to produce a user-
friendly error message .
• 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 if you
want the information from an exception’s traceback but also want an except statement to
gracefully handle the exception. You will need to import Python’s traceback module before
calling this function.
Example:
import traceback
try:
raise Exception('This is the error message.')
except:
errorFile = open('errorInfo.txt', 'w')
errorFile.write(traceback.format_exc())
errorFile.close()
print('The traceback info was written to errorInfo.txt.')
• The 116 is the return value from the write() method, since 116 characters were written to the
file. The traceback text was written to errorInfo.txt.
Assertions
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
Example:
>>> podBayDoorStatus = 'open'
>>> assert podBayDoorStatus == 'open', 'The pod bay doors need to be "open".'
>>> podBayDoorStatus = 'I\'m sorry, Dave. I\'m afraid I can't do that.''
>>> assert podBayDoorStatus == 'open', 'The pod bay doors need to be "open".'
• 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.
Using an Assertion in a Traffic Light Simulation
• Say we’re building a traffic light simulation program. The data structure representing
the stoplights at an intersection is a dictionary with keys 'ns' and 'ew', for the
stoplights facing north-south and east-west, respectively.
• The values at these keys will be one of the strings 'green', ' yellow', or 'red'.
• The code looks like this:
market_2nd = {'ns': 'green', 'ew': 'red'}
mission_16th = {'ns': 'red', 'ew': 'green'}
• These two variables will be for the intersections of Market Street and 2nd Street, and Mission
Street and 16th Street.
• To start the project, you want to write a switchLights() function, which will take an
intersection dictionary as an argument and switch the lights.
• At first, we might think that switchLights() should simply switch each light to the next color
in the sequence: Any 'green' values should change to 'yellow', 'yellow' values should change
to 'red', and 'red' values should change to 'green'.
• Example:
def switchLights(stoplight):
for key in stoplight.keys():
if stoplight[key] == 'green':
stoplight[key] = 'yellow'
elif stoplight[key] == 'yellow':
stoplight[key] = 'red'
elif stoplight[key] == 'red':
stoplight[key] = 'green'
switchLights(market_2nd)
• But if while writing switchLights() you had added an assertion to check that at least one of
the lights is always red, you might have included the following at the bottom of the
function:
assert 'red' in stoplight.values(), 'Neither light is red! ' + str(stoplight)
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.
Logging
• If you’ve ever put a print() statement in your code to output some variable’s value while your
program is running, you’ve used a form of logging to debug your code.
• Logging is a great way to understand what’s happening in your program and in what order its
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.
Using the logging Module
• To enable the logging module to display log messages on your screen as your program runs,
copy the following to the top of your program.
• Example:
import logging
logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s %(levelname)s - (message)s')
• 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.
• Example:
import logging
logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s - %
(message)s')
logging.debug('Start of program')
def factorial(n):
logging.debug('Start of factorial( %)' % (n))
total = 1
for i in range(n + 1):
total *= i
logging.debug('i is ' + str(i) + ', total is ' + str(total))
logging.debug('End of factorial( %)' % (n))
return total
print(factorial(5))
logging.debug('End of program')
• 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 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. Change the for i in range(n + 1): line to for i in range(1, n + 1):, and run the program
again.
Logging Levels:
• 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.
Example
>>> import logging
>>> logging.basicConfig(level=logging.DEBUG, format='%(asctime)s-%(levelname)s-%
(message)s')
>>> logging.debug('some debugging details.')
>>> logging.info('The logging module is working.')
>>> logging.warning('An error message is about to be logged.')
>>> logging.error('An error has occurred.')
>>> logging.critical('The program is unable to recover!')
Disabling Logging
• 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.
• Example:
>>>import logging
>>> logging.basicConfig(level=logging.INFO, format=' %(asctime)s -%(levelname)s %(message)s')
>>> logging.critical('Critical error! Critical error!')
>>> logging.disable(logging.CRITICAL)
>>> logging.critical('Critical error! Critical error!')
>>> logging.error('Error! Error!')
Logging to a File
• Instead of displaying the log messages to the screen, you can write them to a text file. The
logging.basicConfig() function takes a filename keyword argument.
• Example:
>>>import logging
>>>logging.basicConfig(filename='myProgramLog.txt', level=logging.DEBUG, format=' %
(asctime)s - %(levelname)s -%(message)s')
IDLE’s Debugger
• The debugger is a feature of IDLE that allows you to execute your program one line at a time. The
debugger will run a single line of code and then wait for you to tell it to continue.
• To enable IDLE’s debugger, click Debug▸Debugger in the interactive shell window. This will
bring up the Debug Control window.
• When the Debug Control window appears, select all four of the Stack, Locals, Source, and Globals
checkboxes so that the window shows the full set of debug information.
• While the Debug Control window is displayed, any time you run a program from the file editor,
the debugger will pause execution before the first instruction and display the following:
1. The line of code that is about to be executed
2. A list of all local variables and their values
3. A list of all global variables and their values
• The program will stay paused until you press one of the five buttons in the Debug Control
window: Go, Step, Over, Out, or Quit.
Go :Clicking the Go button will cause the program to execute normally until it terminates or
reaches a breakpoint.
Step :Clicking the Step button will cause the debugger to execute the next line of code and then
pause again.
Over :Clicking the Over button will execute the next line of code, similar to the Step button.
However, if the next line of code is a function call, the Over button will “step over” the code in
the function.
Out :Clicking the Out button will cause the debugger to execute lines of code at full speed until it
returns from the current function.
Quit :If you want to stop debugging entirely and not bother to continue executing the rest of the
program, click the Quit button.
Breakpoints
• A breakpoint can be set on a specific line of code and forces the debugger to pause whenever the
program execution reaches that line.
• Example:
import random
heads = 0
for i in range(1, 1001):
if random.randint(0, 1) == 1:
heads = heads + 1
if i == 500:
print('Halfway done!')
print('Heads came up ' + str(heads) + ' times.')