Lecture 2: Directories and Exception Handling
1. Working with Directories
Using the os Module
import os
# Get current working directory
current_dir = os.getcwd()
print(f"Current directory: {current_dir}")
# List directory contents
print("\nDirectory contents:")
for item in os.listdir('.'):
print(f"- {item}")
# Create a new directory
os.mkdir('new_directory')
# Create directories recursively
os.makedirs('path/to/new/directory', exist_ok=True)
# Remove a directory (must be empty)
os.rmdir('new_directory')
# Remove directory tree (use with caution!)
import shutil
shutil.rmtree('path/to/remove', ignore_errors=True)
File and Directory Operations
import os
import shutil
# Check if path exists
path = 'example.txt'
if os.path.exists(path):
print(f"{path} exists!")
# Check if path is a file or directory
if os.path.isfile(path):
print(f"{path} is a file")
elif os.path.isdir(path):
print(f"{path} is a directory")
# Get file size
size = os.path.getsize(path)
print(f"File size: {size} bytes")
# Get file modification time
mod_time = os.path.getmtime(path)
from datetime import datetime
print(f"Last modified: {datetime.fromtimestamp(mod_time)}")
# Copy files
shutil.copy('source.txt', 'destination.txt')
# Move/rename files
os.rename('old_name.txt', 'new_name.txt')
# Delete a file
os.remove('file_to_delete.txt')
2. Walking Directory Trees
import os
def list_files(startpath):
for root, dirs, files in os.walk(startpath):
level = root.replace(startpath, '').count(os.sep)
indent = ' ' * 4 * level
print(f"{indent}{os.path.basename(root)}/")
subindent = ' ' * 4 * (level + 1)
for f in files:
print(f"{subindent}{f}")
# Example usage
list_files('.')
# Find all .py files in a directory tree
def find_py_files(directory):
py_files = []
for root, dirs, files in os.walk(directory):
for file in files:
if file.endswith('.py'):
py_files.append(os.path.join(root, file))
return py_files
# Count lines of code in all Python files
def count_lines(directory):
total_lines = 0
for py_file in find_py_files(directory):
with open(py_file, 'r', encoding='utf-8') as f:
lines = f.readlines()
total_lines += len(lines)
print(f"{py_file}: {len(lines)} lines")
print(f"\nTotal lines of Python code: {total_lines}")
3. Errors vs Exceptions
Key Differences:
- Errors are problems that a program should not try to handle (e.g.,
SyntaxError,IndentationError) - Exceptions are conditions that a program might want to handle (e.g.,
FileNotFoundError,ZeroDivisionError)
Common Built-in Exceptions
| Exception | Description |
|---|---|
Exception |
Base class for all exceptions |
ArithmeticError |
Base class for arithmetic errors |
ZeroDivisionError |
Division or modulo by zero |
FileNotFoundError |
File or directory doesn't exist |
PermissionError |
No permission to access a file |
ValueError |
Incorrect value passed to a function |
TypeError |
Operation on inappropriate type |
IndexError |
Sequence index out of range |
KeyError |
Dictionary key not found |
IOError |
I/O related errors |
4. The Exception Hierarchy
# View the built-in exception hierarchy
def print_exception_hierarchy(exception_class, indent=0):
print(' ' * indent + exception_class.__name__)
for subclass in exception_class.__subclasses__():
print_exception_hierarchy(subclass, indent + 4)
# Print the hierarchy starting from BaseException
print_exception_hierarchy(BaseException)
Key Points:
BaseExceptionis the root class for all exceptionsExceptionis the base class for all built-in, non-system-exiting exceptions- Most user-defined exceptions should inherit from
Exception - System-exiting exceptions like
SystemExitandKeyboardInterruptinherit directly fromBaseException
5. Practical Example: Directory Cleanup Utility
import os
import shutil
from datetime import datetime, timedelta
def cleanup_directory(directory, days_old=30, extensions=None, dry_run=False):
"""
Remove files older than specified days with given extensions.
Args:
directory (str): Directory to clean up
days_old (int): Remove files older than this many days
extensions (list): List of file extensions to consider (None means all)
dry_run (bool): If True, only show what would be deleted
"""
if not os.path.exists(directory):
print(f"Error: Directory '{directory}' does not exist.")
return
if not os.path.isdir(directory):
print(f"Error: '{directory}' is not a directory.")
return
cutoff_time = datetime.now() - timedelta(days=days_old)
total_size = 0
removed_count = 0
print(f"Cleaning up directory: {directory}")
print(f"Removing files older than: {cutoff_time}")
if extensions:
print(f"File extensions: {', '.join(extensions) if extensions else 'All'}")
print("-" * 50)
for root, dirs, files in os.walk(directory, topdown=False):
for file in files:
if extensions and not any(file.lower().endswith(ext.lower()) for ext in extensions):
continue
file_path = os.path.join(root, file)
try:
mod_time = datetime.fromtimestamp(os.path.getmtime(file_path))
if mod_time < cutoff_time:
file_size = os.path.getsize(file_path)
print(f"Removing: {file_path} (Last modified: {mod_time}, Size: {file_size} bytes)")
if not dry_run:
try:
if os.path.isfile(file_path):
os.remove(file_path)
elif os.path.isdir(file_path):
shutil.rmtree(file_path)
removed_count += 1
total_size += file_size
except Exception as e:
print(f" Error removing {file_path}: {e}")
except Exception as e:
print(f" Error processing {file_path}: {e}")
print("-" * 50)
print(f"Total files removed: {removed_count}")
print(f"Total space freed: {total_size / (1024*1024):.2f} MB")
# Example usage
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description='Clean up old files in a directory.')
parser.add_argument('directory', help='Directory to clean up')
parser.add_argument('--days', type=int, default=30,
help='Remove files older than this many days (default: 30)')
parser.add_argument('--ext', nargs='+',
help='File extensions to include (e.g., .tmp .log)')
parser.add_argument('--dry-run', action='store_true',
help='Show what would be deleted without actually deleting')
args = parser.parse_args()
cleanup_directory(
args.directory,
days_old=args.days,
extensions=args.ext,
dry_run=args.dry_run
)