An Autonomous Institute
NEAR ITPB, CHANNASANDRA, BENGALURU – 560 067
Affiliated to VTU,Belagavi
Approved by AICTE, New Delhi
Recognized by UGC under 2(f) & 12(B)
Accredited by NBA & NAAC
DEPARTMENT OF COMPUTER SCIENCE AND ENGINEERING
INTRODUCTION TO PYTHON PROGRAMMING – MVJ22PCLK25B
~PREPARED BY DEEPTHI S S , ASSISTANT PROFESSOR - CSE
Module 4:
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,
Debugging: Raising Exceptions, Getting the Traceback as a String, Assertions, Logging, IDLE‟s
Debugger
Organising Files:
1. Organizing Files: The shutil Module
The shutil (shell utilities) module lets you copy, move, rename, and delete files or entire
directories.
Important shutil functions:
• shutil.copy(source, destination)
Copies the file at source to destination.
If destination is a folder, the file is copied into it.
• shutil.copytree(source_folder, destination_folder)
Recursively copies an entire directory tree.
• shutil.move(source, destination)
Moves a file or folder. Renames if the destination is just a filename.
• shutil.rmtree(path)
Deletes a folder and all its contents.
2. Walking a Directory Tree
Python’s os.walk() function generates the file names in a directory tree by walking the tree
either top-down or bottom-up.
Syntax:
import os
for foldername, subfolders, filenames in os.walk('your_path'):
print(f'Folder: {foldername}')
print(f'Subfolders: {subfolders}')
print(f'Files: {filenames}')
Explanation:
• foldername: Current folder being walked through.
• subfolders: List of subfolders in that folder.
• filenames: List of filenames in that folder.
3. Compressing Files with the zipfile Module
Python’s zipfile module allows you to create, read, write, extract, and list ZIP files.
Creating and Writing ZIP Files:
Reading and Extracting:
4. Project: Renaming Files with American-Style Dates to European-Style Dates
Goal:
Rename files with dates in MM-DD-YYYY format to DD-MM-YYYY format.
Steps:
1. Use os.listdir() and re module to find files with date format.
2. Identify and extract parts: month, day, year, and the rest of the filename.
3. Construct the new filename with European-style date.
4. Rename using os.rename().
Explanation:
There are two files with names :
report_03-25-2024.txt and want to rename as report_25-03-2024.txt
Line by line Exaplanation:
import shutil, os, re
• shutil: for moving (renaming) files.
• os: to list files in the current directory.
• re: for using regular expressions to detect American-style dates.
This regular expression finds files with American-style dates: MM-DD-YYYY.
• ^(.*?): Matches and captures any characters at the start of the filename.
• ((0|1)?\d)-: Matches the month part, allowing one or two digits.
• ((0|1|2|3)?\d)-: Matches the day part.
• ((19|20)\d\d): Matches the year part, starting with 19 or 20.
• (.*?)$: Matches anything after the date.
• The use of re.VERBOSE allows comments and spacing in the regex for readability.
for filename in os.listdir('.'): // Lists every file and folder in the current directory (.).
mo = date_pattern.search(filename)
• mo stands for "match object".
• search() looks for a match of the date pattern in the filename.
• If the pattern is found, mo will contain the matched text and groups.
if mo is None:
continue // If no date is found in the filename, skip to the next file.
before = mo.group(1) // Text before the date.
month = mo.group(2) // The American-style month.
day = mo.group(4) // the day part
year = mo.group(6) // the year part
after = mo.group(8) // text after the date
euro_filename = f'{before}{day}-{month}-{year}{after}' //Constructs the new filename in
European-style date format: DD-MM-YYYY.
shutil.move(filename, euro_filename) // Renames (moves) the file from the original name to the new
name.
example
Project: Backing Up a Folder into a ZIP File
Goal:
Create a ZIP file that contains the entire contents of a folder and ensures previous backups
aren’t overwritten.
Features:
• Automatically names ZIPs like foldername_1.zip, foldername_2.zip, etc.
• Avoids re-zipping ZIP files already in the folder.
• Uses os.walk() to gather all files.
import zipfile, os # Import the modules needed for zipping files and navigating the file system
def backup_to_zip(folder):
# Convert the folder name to an absolute path (for consistency and avoiding confusion)
folder = os.path.abspath(folder)
# Start with a number to create unique backup file names
number = 1
# Keep incrementing the number until we find a filename that doesn't exist yet
while True:
zip_filename = os.path.basename(folder) + f'_{number}.zip' # e.g., 'myfolder_1.zip'
if not os.path.exists(zip_filename): # Check if this ZIP file already exists
break # If not, we can use this name
number += 1 # Otherwise, try the next number
print(f'Creating {zip_filename}...')
# Create the new ZIP file in write mode
backup_zip = zipfile.ZipFile(zip_filename, 'w')
# Walk through every folder, subfolder, and file in the given folder
for foldername, subfolders, filenames in os.walk(folder):
print(f'Adding files in {foldername}...')
# Add the current folder to the ZIP file
backup_zip.write(foldername)
# Loop through each file in the folder
for filename in filenames:
new_base = os.path.basename(folder) + '_' # This helps us detect previous backup files
# Skip any files that are already backup ZIPs made by this script
if filename.startswith(new_base) and filename.endswith('.zip'):
continue # Don’t back up backup ZIP files
# Create the full path of the file and add it to the ZIP
file_path = os.path.join(foldername, filename)
backup_zip.write(file_path)
# Close the ZIP file after writing all files
backup_zip.close()
print('Done.')
Debugging
1. Raising Exceptions
Purpose:
To intentionally cause an error in your code when something goes wrong.
Example & Explanation:
def box_print(symbol, width, height):
if len(symbol) != 1:
raise Exception('Symbol must be a single character string.')
# Custom error if symbol isn't 1 character
if width <= 2:
raise Exception('Width must be greater than 2.') # Width
must allow drawing a box
if height <= 2:
raise Exception('Height must be greater than 2.') #
Height too small to make a box
print(symbol * width) # Top of the box
for i in range(height - 2):
print(symbol + ' ' * (width - 2) + symbol) # Middle rows
print(symbol * width) # Bottom of the box
box_print('*', 4, 4)
Output:
markdown
Copy code
****
* *
* *
****
If input is invalid:
box_print('**', 4, 4)
Raises:
Exception: Symbol must be a single character string.
2. Getting the Traceback as a String
Purpose:
To log or display the full traceback message without crashing the program.
Example:
import traceback
try:
raise Exception('This is the error message.')
except:
error_file = open('error_log.txt', 'w')
error_file.write(traceback.format_exc()) # Get the traceback
as a string and write it to a file
error_file.close()
print('The traceback info was written to error_log.txt')
What It Does:
• traceback.format_exc() captures the error message and stack trace.
• It’s useful in production code to log errors instead of showing them to users.
3. Assertions
Purpose:
To check if a condition is true while the program runs.
If not, an AssertionError is raised.
Example:
pod_bay_door_status = 'open'
assert pod_bay_door_status == 'closed', 'The pod bay doors need to
be "closed".'
Output:
AssertionError: The pod bay doors need to be "closed".
Tip:
Use assertions for sanity checks during development, not for handling runtime user input.
4. Logging
Purpose:
To track and record what the program is doing, especially useful in debugging large programs.
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(f'Start of factorial({n})')
total = 1
for i in range(1, n + 1):
total *= i
logging.debug(f'i = {i}, total = {total}')
logging.debug(f'End of factorial({n})')
return total
print(factorial(5))
logging.debug('End of program')
Output:
(Logged in console)
2025-05-20 18:30:01,123 - DEBUG - Start of program
2025-05-20 18:30:01,123 - DEBUG - Start of factorial(5)
...
2025-05-20 18:30:01,123 - DEBUG - End of program
Logging Levels:
• DEBUG: Details for diagnosing problems.
• INFO: Confirmation that things work.
• WARNING: Something unexpected.
• ERROR: A more serious problem.
• CRITICAL: Program may not continue.
5. IDLE’s Debugger
Purpose:
To step through your code line by line and watch variables change.
How to Use:
1. Open your script in IDLE.
2. From the menu: Debug → Debugger.
3. Run your script.
4. You can now:
o Step through the code (Step button)
o View the call stack and local variables
o Watch execution line-by-line
Example:
If you run this:
def add(x, y):
total = x + y
return total
result = add(5, 7)
print(result)
Using the Debugger:
• You can see x = 5, y = 7, total = 12, and result = 12 as it runs.
Summary Table
Feature Purpose Output Example / Use Case
raise Exception() Stop execution when "Symbol must be a
something’s wrong single character"
traceback.format_exc() Get error details as a Log error to file
string
assert condition Ensure something is true Raises AssertionError if not
during execution
logging.debug() Track what's happening See values, logic, steps during
program execution
IDLE Debugger Step-by-step run and Visual debugging tool
variable view