Atomic file writes for Python with cross-platform support and data integrity guarantees.
npx @tessl/cli install tessl/pypi-atomicwrites@1.4.0Atomicwrites provides atomic file write operations that ensure data integrity during file operations by using temporary files and atomic rename operations. It offers cross-platform support for Windows and POSIX systems, handling platform-specific atomic file operations through appropriate system calls.
pip install atomicwritesfrom atomicwrites import atomic_writeFor class-based API:
from atomicwrites import AtomicWriterFor low-level functions:
from atomicwrites import replace_atomic, move_atomicfrom atomicwrites import atomic_write
# Simple atomic write with context manager
with atomic_write('example.txt', overwrite=True) as f:
f.write('Hello, world!')
# File doesn't exist on disk yet
# Now the file exists atomically
# Prevent overwriting existing files
try:
with atomic_write('example.txt', overwrite=False) as f:
f.write('This will fail')
except OSError as e:
print(f"File exists: {e}")
# Advanced usage with class-based API
from atomicwrites import AtomicWriter
writer = AtomicWriter('config.json', mode='w', overwrite=True)
with writer.open() as f:
f.write('{"setting": "value"}')Atomicwrites uses a temporary file approach to ensure atomicity:
Simple context manager interface for atomic file writes with automatic error handling and cleanup.
def atomic_write(path, writer_cls=AtomicWriter, **cls_kwargs):
"""
Simple atomic writes using context manager.
Parameters:
- path: str, target path to write to
- writer_cls: class, writer class to use (default: AtomicWriter)
- **cls_kwargs: additional keyword arguments passed to writer class
Returns:
Context manager that yields file-like object
Raises:
- ValueError: for invalid file modes
- OSError: when file exists and overwrite=False
- Various OS-specific errors during file operations
"""Flexible class-based API providing fine-grained control over atomic write operations and allowing for customization through subclassing.
class AtomicWriter:
"""
A helper class for performing atomic writes with fine-grained control.
Parameters:
- path: str, destination filepath (may or may not exist)
- mode: str, file mode (default: "wb" on Python 2, "w" on Python 3)
- overwrite: bool, whether to overwrite existing files (default: False)
- **open_kwargs: additional arguments passed to open()
Raises:
- ValueError: for unsupported modes ('a', 'x') or non-write modes
"""
def __init__(self, path, mode=DEFAULT_MODE, overwrite=False, **open_kwargs):
"""Initialize AtomicWriter with target path and options."""
def open(self):
"""
Open the temporary file and return context manager.
Returns:
Context manager that yields file-like object
"""
def get_fileobject(self, suffix="", prefix=tempfile.gettempprefix(), dir=None, **kwargs):
"""
Return the temporary file to use.
Parameters:
- suffix: str, suffix for temporary filename
- prefix: str, prefix for temporary filename
- dir: str, directory for temporary file (defaults to target file's directory)
- **kwargs: additional arguments
Returns:
File-like object for writing
"""
def sync(self, f):
"""
Clear file caches before commit (flush and fsync).
Parameters:
- f: file object to sync
"""
def commit(self, f):
"""
Move temporary file to target location atomically.
Parameters:
- f: file object to commit
"""
def rollback(self, f):
"""
Clean up temporary resources (unlink temp file).
Parameters:
- f: file object to rollback
Raises:
- OSError: if temporary file cannot be removed
"""Direct atomic file operations for advanced use cases requiring precise control over file movement behavior.
def replace_atomic(src, dst):
"""
Move src to dst atomically, overwriting dst if it exists.
Parameters:
- src: str, source file path
- dst: str, destination file path
Requirements:
Both paths must reside on the same filesystem for atomicity.
Platform Implementation:
- POSIX: Uses rename() with directory fsync
- Windows: Uses MoveFileEx() with MOVEFILE_REPLACE_EXISTING flag
Raises:
- OSError: for file system errors
- WinError: on Windows for system-specific errors
"""
def move_atomic(src, dst):
"""
Move src to dst atomically, raises FileExistsError if dst exists.
Parameters:
- src: str, source file path
- dst: str, destination file path
Requirements:
Both paths must reside on the same filesystem for atomicity.
Platform Implementation:
- POSIX: Uses link() + unlink() with directory fsync
- Windows: Uses MoveFileEx() without MOVEFILE_REPLACE_EXISTING
Raises:
- FileExistsError: if destination file already exists
- OSError: for other file system errors
- WinError: on Windows for system-specific errors
Note:
There may be a time window where both filesystem entries exist.
"""import os
from typing import Union
# Module constants
__version__: str = "1.4.1"
DEFAULT_MODE: str # "wb" on Python 2, "w" on Python 3
# Type aliases for clarity
FilePath = Union[str, bytes, os.PathLike]
FileMode = str # Valid modes: 'w', 'wb', 'w+', 'wb+', etc. (no 'a' or 'x')Atomicwrites automatically handles cleanup on exceptions:
from atomicwrites import atomic_write
import os
# Handle overwrite protection
try:
with atomic_write('existing_file.txt', overwrite=False) as f:
f.write('This will fail if file exists')
except FileExistsError:
print("File already exists and overwrite=False")
# Handle permission errors
try:
with atomic_write('/root/protected.txt', overwrite=True) as f:
f.write('This may fail due to permissions')
except PermissionError:
print("Insufficient permissions to write file")
# Handle invalid modes
try:
from atomicwrites import AtomicWriter
writer = AtomicWriter('test.txt', mode='a') # append mode
except ValueError as e:
print(f"Invalid mode: {e}")os.rename() for overwrite operationsos.link() + os.unlink() for non-overwrite operationsfsync() to ensure filename persistencefcntl.F_FULLFSYNC for complete data flushingMoveFileEx() via ctypes with appropriate flagsMOVEFILE_WRITE_THROUGH flag ensures data reaches diskMOVEFILE_REPLACE_EXISTING flag for overwrite operations