Python os.write Function
Last modified April 11, 2025
This comprehensive guide explores Python's os.write
function,
which performs low-level file writing using file descriptors. We'll cover
file descriptors, byte strings, return values, and practical examples.
Basic Definitions
The os.write
function writes bytes to a file descriptor. It's a
low-level operation that bypasses Python's file object layer.
Key parameters: fd (file descriptor), data (bytes-like object). Returns the number of bytes actually written. Requires proper file opening and permissions.
Basic File Writing
This example demonstrates the simplest use of os.write
to write
data to a file. We first open the file to get a file descriptor.
import os # Open file and get file descriptor fd = os.open("output.txt", os.O_WRONLY | os.O_CREAT) # Write bytes to file data = b"Hello, World!\n" bytes_written = os.write(fd, data) print(f"Wrote {bytes_written} bytes") # Close file descriptor os.close(fd)
The code opens a file with write-only access, creates it if needed, writes bytes, and closes the descriptor. Note the 'b' prefix for bytes literal.
Always close file descriptors to prevent resource leaks. The return value shows how many bytes were actually written.
Writing Multiple Chunks
os.write
can be called multiple times to write data in chunks.
This example shows sequential writes to the same file descriptor.
import os fd = os.open("log.txt", os.O_WRONLY | os.O_CREAT | os.O_APPEND) messages = [ b"System started\n", b"Loading modules...\n", b"Initialization complete\n" ] for msg in messages: bytes_written = os.write(fd, msg) print(f"Wrote {bytes_written} bytes") os.close(fd)
We open the file in append mode to preserve existing content. Each string is written separately, with progress reported after each write.
The O_APPEND flag ensures writes go to the end of file, even if other processes are writing to it simultaneously.
Handling Large Files
For large files, it's efficient to write data in fixed-size chunks. This example demonstrates writing a large byte array in smaller pieces.
import os # Generate 1MB of data data = b"X" * (1024 * 1024) fd = os.open("large_file.bin", os.O_WRONLY | os.O_CREAT) chunk_size = 4096 # 4KB chunks total_written = 0 for i in range(0, len(data), chunk_size): chunk = data[i:i + chunk_size] written = os.write(fd, chunk) total_written += written print(f"Written {total_written} bytes", end="\r") os.close(fd) print(f"\nTotal written: {total_written} bytes")
The code writes a 1MB file in 4KB chunks. This approach is memory-efficient and provides progress feedback during the operation.
Note the carriage return (\r) in print to update the same line, creating a progress indicator effect.
Error Handling
os.write
can raise exceptions. This example shows proper error
handling for common scenarios like disk full or permission issues.
import os import errno try: fd = os.open("protected.txt", os.O_WRONLY | os.O_CREAT) data = b"Attempting to write to protected file\n" try: bytes_written = os.write(fd, data) print(f"Successfully wrote {bytes_written} bytes") except OSError as e: if e.errno == errno.ENOSPC: print("Error: No space left on device") elif e.errno == errno.EBADF: print("Error: Bad file descriptor") else: print(f"Write error: {e}") finally: os.close(fd) except PermissionError: print("Error: Permission denied for file creation") except OSError as e: print(f"File open error: {e}")
The example demonstrates nested try-except blocks to handle different error scenarios at both file opening and writing stages.
Specific error numbers (errno) help identify exact failure reasons for appropriate handling.
Non-blocking I/O
With non-blocking file descriptors, os.write
may write fewer
bytes than requested. This example shows how to handle partial writes.
import os import errno # Create a named pipe (run this only once) if not os.path.exists("mypipe"): os.mkfifo("mypipe") # Open in non-blocking mode fd = os.open("mypipe", os.O_WRONLY | os.O_NONBLOCK) data = b"X" * 65536 # Large chunk of data total = len(data) written = 0 try: while written < total: try: n = os.write(fd, data[written:]) if n == 0: print("Write would block") break written += n print(f"Written {written}/{total} bytes") except OSError as e: if e.errno == errno.EAGAIN: print("Resource temporarily unavailable") break raise finally: os.close(fd)
The code demonstrates writing to a named pipe in non-blocking mode. It handles partial writes and EAGAIN errors that occur when the pipe is full.
Non-blocking I/O is useful for applications that must remain responsive while performing I/O operations.
Binary Data Writing
os.write
is ideal for writing binary data. This example shows
how to write structured binary data using the struct module.
import os import struct fd = os.open("data.bin", os.O_WRONLY | os.O_CREAT) # Pack different data types into bytes points = [ struct.pack("ffi", 1.5, 2.5, 10), # x, y, color struct.pack("ffi", 3.1, 4.2, 20), struct.pack("ffi", 5.0, 6.0, 30) ] for point in points: os.write(fd, point) os.close(fd) print("Binary data written successfully")
The example writes three points (each containing two floats and an integer) in binary format. struct.pack converts Python values to bytes.
Binary file writing is common in performance-sensitive applications like games or scientific computing.
Standard Output Writing
File descriptor 1 represents standard output. This example shows direct
writing to stdout using os.write
.
import os import sys # Method 1: Using file descriptor 1 os.write(1, b"Writing to stdout via file descriptor\n") # Method 2: Using sys.stdout's fileno() stdout_fd = sys.stdout.fileno() os.write(stdout_fd, b"Writing via sys.stdout's descriptor\n") # Method 3: Using os.fdopen with os.fdopen(1, "wb") as f: f.write(b"Writing via fdopen wrapper\n")
The example demonstrates three ways to write to stdout. The first uses the well-known descriptor number (1), the second uses sys.stdout's descriptor.
Direct stdout writing can be useful when you need to bypass Python's buffering or work at a lower level.
Security Considerations
- File descriptors: Must be properly opened and closed
- Data validation: Ensure only expected data gets written
- Error handling: Always check return values and handle errors
- Race conditions: File state may change between operations
- Platform differences: Behavior may vary between OSes
Best Practices
- Use context managers: For automatic descriptor closing
- Check return values: Verify all bytes were written
- Prefer buffered I/O: For most high-level applications
- Handle partial writes: Especially in non-blocking mode
- Clean up resources: Always close file descriptors
Source References
Author
List all Python tutorials.