How do I copy a file in Python?
There are several effective methods to copy files in Python, with the most common approaches using the shutil module for comprehensive file operations and the pathlib module for object-oriented path handling. The shutil.copy() method is recommended for most use cases as it handles file content and permissions, while shutil.copy2() preserves all metadata including timestamps. For modern Python development, pathlib provides a more intuitive way to work with file paths alongside traditional shutil functions.
Contents
- Using shutil Module
- Using pathlib Module
- Advanced Copying Techniques
- Error Handling and Best Practices
- Comparing Different Methods
Using shutil Module
The shutil module provides high-level file operations and is the most commonly used approach for file copying in Python. It offers several methods with different capabilities.
shutil.copy()
The shutil.copy() method copies the file content and the file’s permission mode but doesn’t preserve other metadata like creation and modification times.
import shutil
# Basic file copy
source = "source_file.txt"
destination = "destination_folder/"
shutil.copy(source, destination)
According to the official Python documentation, copy() preserves permission modes but not timestamps, making it suitable when you need basic copying with permissions.
shutil.copy2()
For preserving all file metadata including creation and modification times, use shutil.copy2().
import shutil
# Copy with full metadata preservation
source = "important_file.txt"
destination = "backup_folder/"
shutil.copy2(source, destination)
As stated in the official documentation, copy2() is the method to use when you need to preserve all file metadata from the original.
shutil.copyfile()
The shutil.copyfile() method copies only the file content without any metadata or permissions. Importantly, it requires the destination to be a file path, not a directory.
import shutil
# Copy content only, no metadata
shutil.copyfile("data.json", "data_backup.json")
The GeeksforGeeks guide explains that copyfile() is the fastest copying method but doesn’t preserve any file metadata or permissions.
shutil.copyfileobj()
For large files or when working directly with file objects, shutil.copyfileobj() provides customizable buffer size and reads data in chunks to avoid memory issues.
import shutil
# Copy using file objects with buffer size
source_file = open('large_file.bin', 'rb')
dest_file = open('large_file_copy.bin', 'wb')
shutil.copyfileobj(source_file, dest_file, buffer_size=1024*1024) # 1MB buffer
source_file.close()
dest_file.close()
Using pathlib Module
The pathlib module, introduced in Python 3.4, provides an object-oriented approach to file system operations and is the recommended modern way to handle file paths.
Basic pathlib with shutil
You can use shutil methods directly with pathlib.Path objects (Python 3.6+):
from pathlib import Path
import shutil
# Using Path objects with shutil
source_path = Path("source_file.txt")
destination_path = Path("destination_folder/")
shutil.copy(source_path, destination_path)
The Stack Overflow discussion confirms that Python 3.6+ shutil.copy() can handle Path objects seamlessly.
Manual pathlib Copying
For basic needs, you can manually copy files using pathlib methods:
from pathlib import Path
# Copy binary files
source = Path("data.bin")
destination = Path("data_backup.bin")
destination.write_bytes(source.read_bytes())
# Copy text files
source = Path("document.txt")
destination = Path("document_backup.txt")
destination.write_text(source.read_text())
As shown in the SQLPey guide, this approach works well for simple cases but doesn’t preserve all metadata.
Future pathlib Methods
Python 3.14 will add dedicated copy and move methods to pathlib.Path objects, eliminating the need to use shutil:
# This will be available in Python 3.14
source = Path("source.txt")
destination = Path("destination.txt")
source.copy(destination) # Future method
The Trey Hunner blog mentions that Python 3.14 will add copy, copy_into, move, and move_into methods to pathlib.Path objects.
Advanced Copying Techniques
Copying with Error Handling
Robust file copying should include proper error handling:
import shutil
from pathlib import Path
def safe_copy_file(source, destination):
"""Copy file with comprehensive error handling"""
try:
source_path = Path(source)
dest_path = Path(destination)
# Check if source exists
if not source_path.exists():
raise FileNotFoundError(f"Source file not found: {source}")
# Create destination directory if it doesn't exist
dest_path.parent.mkdir(parents=True, exist_ok=True)
# Copy the file
shutil.copy2(source_path, dest_path)
return True
except Exception as e:
print(f"Error copying file: {e}")
return False
# Usage
safe_copy_file("config.json", "backups/config_backup.json")
Copy and Rename
To copy a file and rename it simultaneously:
import shutil
import os
from pathlib import Path
def copy_and_rename(source_file, destination_dir, new_name):
"""Copy a file to a directory with a new name"""
try:
# Create destination directory if it doesn't exist
os.makedirs(destination_dir, exist_ok=True)
# Full destination path
destination_path = os.path.join(destination_dir, new_name)
# Copy with metadata preservation
shutil.copy2(source_file, destination_path)
print(f"Successfully copied {source_file} to {destination_path}")
return destination_path
except Exception as e:
print(f"Error in copy and rename: {e}")
return None
# Usage
copy_and_rename("data.csv", "processed_data/", "processed_data.csv")
This approach is demonstrated in the GeeksforGeeks guide.
Large File Copying
For very large files, consider using chunked copying:
def copy_large_file(source_path, dest_path, buffer_size=1024*1024):
"""Copy large files with progress tracking"""
source = Path(source_path)
dest = Path(dest_path)
if not source.exists():
raise FileNotFoundError(f"Source file not found: {source_path}")
dest.parent.mkdir(parents=True, exist_ok=True)
file_size = source.stat().st_size
copied = 0
with open(source, 'rb') as src_file, open(dest, 'wb') as dst_file:
while True:
chunk = src_file.read(buffer_size)
if not chunk:
break
dst_file.write(chunk)
copied += len(chunk)
# Optional: Print progress
progress = (copied / file_size) * 100
print(f"\rProgress: {progress:.1f}%", end="")
print() # New line after progress
Error Handling and Best Practices
Essential Error Handling
Always implement proper error handling to prevent unexpected behavior:
import shutil
from pathlib import Path
def robust_copy(source, destination):
"""Robust file copying with comprehensive error handling"""
try:
src = Path(source)
dst = Path(destination)
# Validate source exists
if not src.exists():
raise FileNotFoundError(f"Source file does not exist: {source}")
# Check source is actually a file
if not src.is_file():
raise ValueError(f"Source is not a file: {source}")
# Create parent directories if needed
dst.parent.mkdir(parents=True, exist_ok=True)
# Check destination exists and handle appropriately
if dst.exists():
if dst.is_dir():
# If destination is directory, append filename
dst = dst / src.name
else:
# Otherwise, confirm overwrite
response = input(f"File {destination} already exists. Overwrite? (y/n): ")
if response.lower() != 'y':
print("Copy operation cancelled.")
return False
# Perform the copy
shutil.copy2(src, dst)
print(f"Successfully copied {source} to {destination}")
return True
except PermissionError:
print(f"Permission denied when copying to {destination}")
except OSError as e:
print(f"OS error occurred: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
return False
Best Practices
According to the ZetCode guide, follow these best practices:
- Use shutil for most cases: It handles edge cases properly
- Check file existence first: Avoid accidental overwrites
- Handle paths carefully: Use
pathlibor raw strings for Windows paths - Preserve metadata: Use
copy2()when timestamps matter - Use pathlib for path manipulation: It’s more readable and cross-platform
Cross-Platform Considerations
For cross-platform compatibility:
from pathlib import Path
import shutil
def cross_platform_copy(source, destination):
"""Cross-platform file copying with path handling"""
try:
# Use pathlib for path handling
src = Path(source)
dst = Path(destination)
# Use resolve() for absolute paths
src = src.resolve()
dst = dst.resolve()
# Handle Windows paths with care
if dst.exists() and dst.is_dir():
dst = dst / src.name
# Perform the copy
shutil.copy2(src, dst)
return True
except Exception as e:
print(f"Cross-platform copy failed: {e}")
return False
Comparing Different Methods
Method Comparison Table
| Method | Preserves Permissions | Preserves Metadata | Works with Directories | Best For |
|---|---|---|---|---|
shutil.copy() |
✅ | ❌ | ✅ | Basic copying with permissions |
shutil.copy2() |
✅ | ✅ | ✅ | Complete file preservation |
shutil.copyfile() |
❌ | ❌ | ❌ | Fast content-only copying |
shutil.copyfileobj() |
❌ | ❌ | ❌ | Large files, custom buffering |
pathlib + read_bytes() |
❌ | ❌ | ❌ | Simple binary files |
pathlib + read_text() |
❌ | ❌ | ❌ | Simple text files |
Performance Considerations
For different use cases, different methods perform better:
import time
from pathlib import Path
import shutil
def benchmark_copy_methods():
"""Benchmark different copy methods"""
# Create a test file
test_file = Path("test_file.txt")
test_file.write_text("A" * 1024*1024) # 1MB file
methods = [
("shutil.copy", lambda: shutil.copy(test_file, "copy_method.txt")),
("shutil.copy2", lambda: shutil.copy2(test_file, "copy2_method.txt")),
("shutil.copyfile", lambda: shutil.copyfile(test_file, "copyfile_method.txt")),
("pathlib read_bytes", lambda: Path("pathlib_method.txt").write_bytes(test_file.read_bytes())),
("shutil.copyfileobj", lambda:
open(test_file, 'rb').read() and open("copyobj_method.txt", 'wb').write(open(test_file, 'rb').read()))
]
results = {}
for name, method in methods:
start = time.time()
method()
end = time.time()
results[name] = end - start
# Clean up
Path(f"{name.replace('.', '_')}_method.txt").unlink(missing_ok=True)
# Print results
print("Copy Method Performance:")
for method, time_taken in sorted(results.items(), key=lambda x: x[1]):
print(f"{method}: {time_taken:.4f} seconds")
# Clean up test file
test_file.unlink()
# Run benchmark
benchmark_copy_methods()
When to Use Each Method
Based on the research findings:
- Use
shutil.copy()when you need basic file copying with permission preservation - Use
shutil.copy2()when you need complete file metadata preservation - Use
shutil.copyfile()when you only need content and performance is critical - Use
shutil.copyfileobj()for very large files or custom buffer handling - Use
pathlibmethods when working with file paths extensively - Wait for Python 3.14 if you want native pathlib copy methods
The Real Python guide suggests that while pathlib is tempting for everything path-related, you should consider using shutil for copying files as it’s more robust and handles edge cases properly.
Sources
- Python’s shutil Module Documentation - Official Python documentation for high-level file operations
- Python’s pathlib Module: Taming the File System – Real Python - Comprehensive guide to pathlib module and best practices
- Copy file with pathlib in Python - Stack Overflow - Discussion on using pathlib with shutil methods
- Python | shutil.copyfile() method - GeeksforGeeks - Detailed explanation of copyfile method
- Python | shutil.copy() method - GeeksforGeeks - Examples and usage of copy method
- Copy Files and Rename in Python - GeeksforGeeks - Advanced copying with renaming functionality
- Why you should be using pathlib - Trey Hunner - Insights on pathlib benefits and future developments
- Solved: Top 5 Methods to Copy Files Using Pathlib in Python - Alternative approaches to file copying
- How To Use Shutil.copy() Method In Python - Practical examples and use cases
- Copying Files in Python: Methods, Error Handling, & More - Comprehensive guide with error handling
Conclusion
Copying files in Python can be accomplished through several effective methods, each with specific advantages. The shutil module provides robust, production-ready file copying capabilities with methods like copy() for basic operations and copy2() for complete metadata preservation. For modern Python development, pathlib offers an intuitive object-oriented approach that integrates well with shutil functions.
Key takeaways include:
- Choose the right method: Use
shutil.copy2()when metadata preservation is crucial,shutil.copy()for basic operations, andshutil.copyfile()for performance-critical content-only copying - Implement error handling: Always include proper exception handling to prevent unexpected failures and provide meaningful error messages
- Consider file size: For very large files, use
shutil.copyfileobj()with appropriate buffer sizes to manage memory efficiently - Use pathlib for path manipulation: Leverage
pathlib.Pathobjects for cleaner, more readable code that’s cross-platform compatible - Plan for Python 3.14: The upcoming native pathlib copy methods will simplify file operations further
For most use cases, starting with shutil.copy2() and adding pathlib for path handling provides the best combination of robustness and readability. Remember to always check file existence, handle permissions appropriately, and consider cross-platform compatibility in your file operations.