Chapter - 1 Organizing Files:: Shutil
Chapter - 1 Organizing Files:: Shutil
MODULE 4
Chapter – 1 Organizing Files:
The shutil Module
The shutil (short for shell utilities) module in Python helps you work with files and folders. You can use it
to copy, move, rename, and delete files and folders.
Functions in shutil
1. Copying Files
Example:
import shutil
shutil.copy('C:/example.txt', 'C:/backup_folder')
# Copies file to the folder
shutil.copy('C:/example.txt', 'C:/backup_folder/example_copy.txt')
# Copies and renames it
Example:
shutil.copytree('C:/projects', 'C:/backup_projects')
# Creates a backup of the projects folder
3. Moving and Renaming Files
Examples:
o Moving a file:
shutil.move('C:/example.txt', 'C:/new_folder')
# Moves example.txt to new_folder
o Moving and renaming a file:
shutil.move('C:/example.txt', 'C:/new_folder/renamed_example.txt')
# Moves and renames it
o Renaming a file (no folder):
shutil.move('C:/example.txt', 'C:/new_name.txt') # Renames the file
If you move a file to a folder that already has a file with the same name, it overwrites the existing
file. Be careful!
If the destination folder doesn't exist, Python throws an error:
shutil.move('C:/example.txt', 'C:/nonexistent_folder/new_example.txt')
# FileNotFoundError: [Errno 2] No such file or directory
# Paths
source = Path.home() / 'example.txt' # Your home directory + example.txt
destination_folder = Path.home() / 'backup_folder'
Ex:
import shutil
# Create a directory tree copy
shutil.copytree('C:\\Users\\user\\s', 'C:\\Users\\user\\m')
print("Directory tree copied.")
EX:
import shutil
import os
Example :
import shutil
usage = shutil.disk_usage("C:\\Users\\user")
print(f"Total: {usage.total}, Used: {usage.used}, Free: {usage.free}")
8. shutil.copy2(src, dst)
Similar to shutil.copy, but preserves metadata like timestamps.
Example:
import shutil
Python provides multiple ways to delete files and folders. Here’s an overview of the options:
Examples:
import os
os.unlink('example.txt') # Deletes the file
os.rmdir('empty_folder') # Deletes the empty folder
Example:
import shutil
shutil.rmtree('folder_with_contents') #Deletes the folder and all its files/subfolders
These functions perform permanent deletion. Double-check paths before using them to avoid accidental
data loss.
To avoid unintended deletion, comment out the delete function and use print() to confirm the files or
folders before running the actual delete operation.
Example:
The send2trash module sends files and folders to the recycle bin instead of permanently deleting them.
This allows for recovery if needed.
import send2trash
Advantages:
Safer than os or shutil functions.
Files can be recovered from the recycle bin if deleted by mistake.
Permanent Delete: When disk space is limited, or you are confident about the deletion.
Safe Delete: During development or when there’s a risk of deleting important files by mistake.
1 Input
2 Returns
For each folder it walks through, it returns three values:
3 Usage
Typically used in a for loop to recursively traverse all folders, subfolders, and files in a directory
tree.
Example:
Example Program
Folder structure :
Figure 10-1: An example folder that contains three folders and four files
Output;
Applications of os.walk()
This makes os.walk() a versatile tool for handling directory-related tasks efficiently.
>>> import zipfile, os # Importing the zipfile and os modules to work with ZIP files and file
paths
>>> from pathlib import Path # Importing Path from pathlib to handle file paths
>>> p = Path.home() # Getting the home directory path
>>> exampleZip = zipfile.ZipFile(p/'example.zip') # Opening the ZIP file located in the home
directory
>>> exampleZip.namelist() # Listing all files and directories inside the ZIP file
['spam.txt', 'cats/', 'cats/catnames.txt', 'cats/zophie.jpg']
>>> spamInfo.compress_size # Displaying the compressed size of 'spam.txt' inside the ZIP
3828
A ZipFile object has a namelist() method that returns a list of strings for all the files and folders contained in
the ZIP file. These strings can be passed to the getinfo() ZipFile method to return a ZipInfo object about that
particular file. 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. While a ZipFile object represents an entire
archive file, a ZipInfo object holds useful information about a single file in the archive.
The command at ➊ calculates how efficiently example.zip is compressed by dividing the original file size by the
compressed file size and prints this information.
After running this code, the contents of example.zip will be extracted to C:\. Optionally, you can pass a folder
name to extractall() to have it extract the files into a folder other than the current working directory. If the folder
passed to the extractall() method does not exist, it will be created. For instance, if you replaced the call
at ➊ with exampleZip.extractall('C:\\delicious'), the code would extract the files from example.zip into a
newly created C:\delicious folder.
The extract() method for ZipFile objects will extract a single file from the ZIP file. Continue the interactive
shell example:
>>> exampleZip.extract('spam.txt')
'C:\\spam.txt'
>>> exampleZip.extract('spam.txt', 'C:\\some\\new\\folders')
'C:\\some\\new\\folders\\spam.txt'
>>> exampleZip.close()
The string you pass to extract() must match one of the strings in the list returned by namelist(). Optionally,
you can pass a second argument to extract() to extract the file into a folder other than the current working directory.
If this second argument is a folder that doesn’t yet exist, Python will create the folder. The value
that extract() returns is the absolute path to which the file was extracted.
This code will create a new ZIP file named new.zip that has the compressed contents of spam.txt.
Keep in mind that, just as with writing to files, write mode will erase all existing contents of a ZIP file. If you want
to simply add files to an existing ZIP file, pass 'a' as the second argument to zipfile.ZipFile() to open the ZIP
file in append mode.
# renameDates.py - Renames filenames with American MM-DD-YYYY date format to European DD-MM-YYYY.
PROGRAM
Goal: Add the folder and all its files (except for other ZIP files) to the newly created ZIP file.
How it's done: The program uses os.walk() to traverse the entire directory tree. It adds the current
folder and all files to the ZIP file. Files that are already ZIP files (such as previous backups) are
skipped.
Program
Explanation :
1. os.path.abspath(folder): Ensures the folder path is absolute, making it easier to work with
regardless of the current working directory.
2. zipfile.ZipFile(zipFilename, 'w'): This creates the ZIP file, opening it in write mode ('w'). If
the file already exists, it will be overwritten.
3. os.walk(folder): This function walks through the directory tree, starting from the given folder. It
returns three variables for each directory it visits:
foldername: The current directory's path.
subfolders: A list of subfolders in the current directory (not used here).
filenames: A list of files in the current directory.
4. backupZip.write(): Adds a folder or file to the ZIP file.
5. Skipping Backup ZIP Files: The program skips any files that are already ZIP backups by checking
if the filename ends with .zip.
1. The program first checks if a ZIP file with the desired name already exists by appending a number to
the folder's base name (e.g., AlsPythonBook_1.zip, AlsPythonBook_2.zip, etc.). It increments the
number until it finds a name that doesn’t exist.
2. It then creates the ZIP file using zipfile.ZipFile in write mode.
3. The program uses os.walk() to go through every folder and file in the specified directory and its
subdirectories. Each folder and file is added to the ZIP file, except for any existing ZIP backups.
4. After all files and folders are added, the ZIP file is closed, and the program prints "Done." to indicate
the backup is complete.
Output Example:
If you run the program with the folder C:\delicious, the output will look like this:
Creating delicious_1.zip...
Adding files in C:\delicious...
Adding files in C:\delicious\cats...
Adding files in C:\delicious\waffles...
Adding files in C:\delicious\walnut...
Done.
The next time you run the program, it will create delicious_2.zip and so on.
This program automates the backup of a folder by creating a ZIP file with a unique, incrementing name each
time it's run. It ensures that no previous backups are overwritten and efficiently handles the task of backing
up all files and subfolders.
Chapter – 2
Debugging
Raising Exceptions,
In Python, exceptions are used to handle errors that arise during the execution of a program. However, you
can also raise your own exceptions when you want to enforce specific conditions or handle unexpected
situations. Raising an exception is a way of stopping the execution of the current function and transferring
the control to the nearest exception handler (the except block).
To raise an exception in Python, you use the raise keyword, followed by an instance of the Exception
class, optionally passing an error message.
Syntax:
raise Exception(‘Wrong password. Try again or click ‘Forgot password’ to reset it. ')
When an exception is raised and it is not handled within the same block of code (via a try and
except statement), Python will terminate the program and display the exception's error message.
Example code;
OUTPUT
What is a Traceback?
When Python encounters an error, it generates a traceback, which is a detailed error report.
A traceback includes:
o Error message: Description of the error.
o Line number: Indicates where the error occurred.
o Call stack: The sequence of function calls leading to the error.
The call stack is helpful for debugging, especially when functions are called from multiple places.
Example of Traceback:
OUTPUT
1. Error Message:
The last line of the traceback shows the type of error and a brief description:
ZeroDivisionError: division by zero.
2. Line Number:
The traceback points out the exact lines where the error occurred:
o Line 2 in the divide_numbers function: return a / b.
o Line 9 in the calculate function: result = divide_numbers(num1, num2).
o Line 12 in the main script: calculate().
3. Call Stack:
The sequence of function calls leading to the error is shown step-by-step:
o The program started by calling calculate().
o Inside calculate, the function divide_numbers was called.
o Inside divide_numbers, the error occurred when trying to divide by zero.
This traceback is extremely useful for pinpointing where and why the error happened, making debugging
easier.
Using traceback.format_exc():
I. A traceback helps identify where an error occurred and the sequence of calls leading to it.
II. Use traceback.format_exc() to capture traceback details as a string.
III. Writing traceback to a file is a good debugging practice, ensuring the program doesn’t crash
unexpectedly.
ASSERTION
What is an Assertion?
An assertion is a "sanity check" used during programming to ensure everything is working as expected.
If an assertion condition fails, the program stops immediately and raises an AssertionError.
Example: "In traffic lights, at least one direction must have a red light" — you can use an assertion to
confirm this.
Syntax of Assertion:
Explanation:
Explanation:
Problem Statement:
In a traffic light simulation, at least one light (either north-south or east-west) must always be red to
prevent accidents.
def switch_lights(lights):
for key in lights.keys():
if lights[key] == "green":
lights[key] = "yellow"
elif lights[key] == "yellow":
lights[key] = "red"
elif lights[key] == "red":
lights[key] = "green"
# Example
traffic_lights = {
"north-south": "green",
"east-west": "red"
}
Explanation:
Points TO REMEMBER
1. Purpose of Assertions:
o Assertions are used to detect programmer errors, not user errors.
o They ensure assumptions in your code are correct during development.
2. When to Use Assertions:
o During development to catch bugs early.
o To verify assumptions, e.g., function inputs/outputs.
3. Not for Production:
o Assertions can be disabled in production using the -O flag.
4. Limitations:
o Assertions are not a replacement for testing. Comprehensive testing is still necessary.
o Assertions only check specific conditions you define.
Logging;
Explanation of Logging in Python
Logging is a critical aspect of software development, especially for debugging and monitoring the execution
of your program. It helps you track events in the program, gather information about its flow, and understand
the program's state at various points during execution. Here's an overview of the key concepts and best
practices associated with logging in Python:
What is Logging?
Logging allows developers to keep track of the execution of their program by recording events, errors, and
general information with custom messages. It helps monitor and debug code by providing insights into what
happened during the program's execution, and it can be saved for later analysis.
1. Tracking Program Flow: Logging provides a trail of events showing how your program progresses
through different stages.
2. Recording Variable States: By logging variable values, you can understand the state of your
program at any given point.
3. Easier Debugging: Instead of using print statements (which require manual cleanup), logging leaves
a persistent trail of messages that can help you debug issues.
Python provides a built-in logging module, which simplifies the process of logging messages. The steps for
setting it up are as follows:
1. Basic Setup
You start by importing the logging module and configuring it to log messages:
import logging
level: Specifies the lowest severity level of messages you want to log. Common levels include:
Example of Logging:
import logging
logging.basicConfig(level=logging.DEBUG)
logging.debug('This is a debug message.')
logging.info('This is an info message.')
logging.warning('This is a warning message.')
logging.error('This is an error message.')
logging.critical('This is a critical message.')
Best Practices:
logging.basicConfig(filename='app.log', level=logging.DEBUG)
logging.disable(logging.CRITICAL)
The logging module in Python provides a flexible framework for logging messages from a program, which
helps in tracking its execution and debugging. It is a built-in module and offers several features for efficient
logging.
1. Different Logging Levels: The logging module supports different log levels, which allow you to
control the verbosity of the logs based on their importance. These levels are:
DEBUG: Provides detailed information for troubleshooting.
INFO: For general information about the program's progress.
WARNING: For potential issues or warnings.
ERROR: For serious problems that affect the program's operation.
CRITICAL: For fatal errors that cause the program to stop.
# Logging an exception
try:
1 / 0
except ZeroDivisionError:
logging.exception("Error occurred during division")
The logging module provides robust support for tracking events, debugging, and managing logs in Python
programs. It offers multiple levels of logging, configurable handlers, exception tracking, and the ability to
control the output location. This makes it a powerful tool for maintaining large-scale applications and
ensuring smooth program execution.
IDLE, the Integrated Development and Learning Environment, comes with a built-in debugger that allows
you to step through your Python program line by line. This tool is particularly helpful for identifying and
fixing bugs by closely monitoring variable values and program execution flow.
1. Breakpoints
o A breakpoint is a marker on a specific line of code where the debugger will pause execution.
o In IDLE, breakpoints can be set by right-clicking on the desired line number in the code
editor and selecting “Set Breakpoint” or using the “Breakpoint” menu option.
o Execution pauses at the breakpoint, allowing you to examine the program state before the line
is executed.
2. Stepping Through Code
o Step: Executes the current line and pauses at the next line. If the current line calls a function,
the debugger steps into that function.
o Over: Executes the current line but skips stepping into any called functions, pausing at the
next line after the function call.
o Out: If inside a function, this steps out and continues execution until the calling function
resumes.
3. Continuing Execution
o Once paused, clicking the Continue button resumes normal execution until the next
breakpoint or program termination.
4. Inspecting Variables
oWhile paused, you can view and inspect variables in the current scope using the “Debug
Control” window. It displays the variable names and their current values.
5. Stopping Debugging
o To terminate the program and exit debugging mode, use the Quit button in the Debug
Control window.
1. Start Debugging
o Open your Python script in IDLE.
o Click Debug > Debugger to enable the Debug Control panel.
o Start the program using Run > Run Module (F5). The Debugger will now control execution.
2. Set Breakpoints
o Right-click the desired line number in the editor and select Set Breakpoint.
o Alternatively, use Debug > Set Breakpoint.
3. Run and Pause
o Run the program. Execution pauses at the first breakpoint or the beginning of the program if
no breakpoints are set.
4. Step Through Code
o Use the buttons in the Debug Control panel:
Step to step into code.
Over to skip stepping into function calls.
Out to exit the current function.
5. Inspect and Modify Variables
o Check variable values in the Debug Control panel.
o Modify variable values if needed to test scenarios dynamically.
6. Continue or Quit
o Use Continue to proceed with the execution.
o Use Quit to stop debugging.
Steps:
Debugging in IDLE provides a powerful way to understand your program and fix errors systematically.
Important questions;