Complete guide to Python's os.replace function covering file renaming, atomic replacements, and practical examples.
Last modified April 11, 2025
This comprehensive guide explores Python’s os.replace function, which atomically replaces files. We’ll cover file renaming, cross-device moves, and practical file system operation examples.
The os.replace function atomically replaces the destination file with the source file. It works across different filesystems and handles most edge cases better than os.rename.
Key parameters: src (source file path), dst (destination file path). On Unix, this is equivalent to rename(2), while on Windows it uses MoveFileEx.
The simplest use of os.replace replaces one file with another. If the destination exists, it will be overwritten atomically.
basic_replace.py
import os
with open(“source.txt”, “w”) as f: f.write(“This is the new content”)
with open(“destination.txt”, “w”) as f: f.write(“This is the old content”)
os.replace(“source.txt”, “destination.txt”)
with open(“destination.txt”) as f: print(f.read()) # Output: This is the new content
This example creates two files, then replaces the destination with the source. The operation is atomic - it either completely succeeds or fails.
After replacement, the source file no longer exists, and the destination contains the source file’s content.
os.replace can move files between directories as long as they’re on the same filesystem. This is more reliable than os.rename for cross-directory moves.
cross_directory.py
import os
os.makedirs(“source_dir”, exist_ok=True) os.makedirs(“dest_dir”, exist_ok=True)
with open(“source_dir/file.txt”, “w”) as f: f.write(“File content”)
os.replace(“source_dir/file.txt”, “dest_dir/file.txt”)
print(os.listdir(“source_dir”)) # Output: [] print(os.listdir(“dest_dir”)) # Output: [‘file.txt’]
This moves a file from one directory to another atomically. The operation will fail if the directories are on different filesystems.
Unlike shutil.move, os.replace doesn’t fall back to copy+delete when moving across filesystems.
A common pattern is to write to a temporary file then atomically replace the target. This ensures the target is always in a consistent state.
atomic_update.py
import os import tempfile
def update_file(filename, content): # Create temp file in same directory for atomic replace temp = tempfile.NamedTemporaryFile( dir=os.path.dirname(filename), delete=False )
try:
# Write new content to temp file
with temp:
temp.write(content.encode())
# Atomically replace target file
os.replace(temp.name, filename)
except:
# Clean up temp file if replace fails
os.unlink(temp.name)
raise
update_file(“important.log”, “New log data”)
This pattern ensures the target file always contains complete data, even if the system crashes during the write operation.
The temp file is created in the same directory as the target to ensure they’re on the same filesystem.
os.replace will overwrite existing files, but you may want to check permissions or existence first. This example shows proper handling.
existing_files.py
import os import stat
def safe_replace(src, dst): # Check source exists and is readable if not os.access(src, os.R_OK): raise PermissionError(f"Cannot read source file {src}")
# Check destination directory is writable
dst_dir = os.path.dirname(dst) or "."
if not os.access(dst_dir, os.W_OK):
raise PermissionError(f"Cannot write to {dst_dir}")
# Check if destination exists and is writable
if os.path.exists(dst) and not os.access(dst, os.W_OK):
raise PermissionError(f"Cannot overwrite {dst}")
# Perform the atomic replace
os.replace(src, dst)
try: safe_replace(“new_data.txt”, “existing_data.txt”) except PermissionError as e: print(f"Error: {e}")
This wrapper function adds safety checks before performing the replace operation. It verifies all necessary permissions are available.
Note that between the checks and the actual replace, conditions could change (TOCTOU race condition).
On Unix systems, os.replace may fail when moving between filesystems. This example shows how to handle such cases.
cross_device.py
import os import shutil
def cross_device_replace(src, dst): try: os.replace(src, dst) except OSError as e: if e.errno == 18: # EXDEV - cross-device link # Fall back to copy + delete shutil.copy2(src, dst) os.unlink(src) else: raise
cross_device_replace("/mnt/volume1/file.txt", “/mnt/volume2/file.txt”)
This function attempts a direct replace first, then falls back to copy+delete if the files are on different devices.
The shutil.copy2 preserves metadata, making it the best choice for a fallback operation.
While os.replace primarily works with files, it can also replace empty directories. This example demonstrates directory operations.
directory_replace.py
import os
os.makedirs(“old_dir”, exist_ok=True) os.makedirs(“new_dir”, exist_ok=True)
with open(“old_dir/file1.txt”, “w”) as f: f.write(“Old content”)
with open(“new_dir/file1.txt”, “w”) as f: f.write(“New content”)
try: os.replace(“new_dir”, “old_dir”) except OSError as e: print(f"Error: {e}") # Output: [Errno 39] Directory not empty
os.makedirs(“empty_dir”, exist_ok=True) os.replace(“empty_dir”, “old_dir”) # Fails if old_dir not empty
This shows that directory replacement only works when the target is empty. For non-empty directories, consider shutil.rmtree first.
The behavior is platform-dependent, with Windows being more restrictive about directory operations.
os.replace can raise several exceptions that should be handled properly in production code. This example covers common cases.
error_handling.py
import os import errno
def robust_replace(src, dst): try: os.replace(src, dst) except FileNotFoundError: print(f"Source file {src} does not exist") except PermissionError: print(f"Permission denied for {src} or {dst}") except OSError as e: if e.errno == errno.EXDEV: print(“Cannot replace across filesystems”) elif e.errno == errno.ENOTEMPTY: print(“Target directory is not empty”) else: print(f"Unexpected error: {e}") except Exception as e: print(f"Unexpected error: {e}")
robust_replace(“nonexistent.txt”, “target.txt”) robust_replace("/root/file.txt", “target.txt”) robust_replace("/mnt/fs1/file.txt", “/mnt/fs2/file.txt”)
This function handles the most common error cases for os.replace, providing meaningful error messages for each scenario.
Proper error handling is crucial for file operations that might fail due to permissions or filesystem constraints.
Atomic operations: os.replace ensures the operation is all-or-nothing
Permission checks: Always verify permissions before operations
Symlink handling: Behavior varies by platform with symlinks
Cross-device: May require fallback to copy+delete on Unix
Windows limitations: More restrictive about open file replacement
Use for atomic writes: Write to temp file then replace
Check permissions: Verify access rights before operations
Handle errors: Implement proper error recovery
Cross-platform: Test behavior on all target platforms
Document assumptions: Note filesystem requirements
My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.
List all Python tutorials.