Complete guide to Python's os.fdopen function covering file descriptor conversion, file object creation, and practical examples.
Last modified April 11, 2025
This comprehensive guide explores Python’s os.fdopen function, which creates a file object from a file descriptor. We’ll cover descriptor handling, file object creation, and practical low-level I/O examples.
The os.fdopen function creates a Python file object from an existing file descriptor (fd). It provides higher-level file operations on low-level descriptors.
Key parameters: fd (file descriptor), mode (optional file mode string), buffering (optional buffer size). Returns a file object wrapping the fd.
This example demonstrates the simplest use of os.fdopen to convert a file descriptor into a file object. We first create a descriptor using os.open.
basic_conversion.py
import os
fd = os.open(“example.txt”, os.O_RDWR | os.O_CREAT)
file_obj = os.fdopen(fd, “r+”)
file_obj.write(“Hello World\n”) file_obj.seek(0) print(file_obj.read())
file_obj.close()
The example shows the complete lifecycle: create descriptor, convert to file object, perform I/O, and close. Note that closing the file object also closes the underlying descriptor.
This approach is useful when you need both low-level descriptor operations and high-level file object methods in the same program.
By default, os.fdopen closes the descriptor when the file object is closed. We can prevent this using closefd=False parameter.
preserve_descriptor.py
import os
fd = os.open(“data.txt”, os.O_RDWR | os.O_CREAT)
file_obj = os.fdopen(fd, “r+”, closefd=False)
file_obj.write(“Preserved descriptor\n”) file_obj.close()
os.write(fd, b"Additional data\n") os.close(fd) # Must close manually
This example keeps the descriptor valid after closing the file object. This is useful when you need to continue using the descriptor after file object operations.
Remember to manually close the descriptor when done to avoid resource leaks.
os.fdopen can wrap standard stream descriptors (0, 1, 2) for more convenient I/O operations. This example demonstrates wrapping stdin.
std_streams.py
import os import sys
stdin = os.fdopen(0, “r”) # Equivalent to sys.stdin
print(“Enter your name:”) name = stdin.readline().strip() print(f"Hello, {name}!")
stdout = os.fdopen(1, “w”) # Equivalent to sys.stdout stdout.write(f"Greetings, {name}!\n") stdout.flush()
The example shows how to create file objects for standard input and output. This can be useful in situations where sys.stdin/stdout are not available.
Note that these file objects share the same underlying descriptors as sys.stdin/stdout, so operations affect both.
os.fdopen supports different buffering modes like regular open(). This example demonstrates line buffering and no buffering.
buffering_modes.py
import os
fd = os.open(“output.log”, os.O_WRONLY | os.O_CREAT)
line_buffered = os.fdopen(fd, “w”, buffering=1) line_buffered.write(“Line 1\n”) # Flushed immediately line_buffered.write(“Line 2\n”)
fd2 = os.open(“direct.log”, os.O_WRONLY | os.O_CREAT) unbuffered = os.fdopen(fd2, “w”, buffering=0) unbuffered.write(“Immediate write”) unbuffered.close()
The first file object uses line buffering (1), flushing after each newline. The second uses no buffering (0), writing directly to disk.
Buffering choices affect performance and data safety - unbuffered is slower but ensures immediate writes.
This example demonstrates proper error handling when working with file descriptors and os.fdopen. We check for invalid descriptors.
error_handling.py
import os import errno
try: # Try to open invalid descriptor bad_fd = 999 file_obj = os.fdopen(bad_fd, “r”) except OSError as e: if e.errno == errno.EBADF: print(“Error: Bad file descriptor”) else: print(f"Unexpected error: {e}")
try: fd = os.open(“data.txt”, os.O_RDONLY) file_obj = os.fdopen(fd, “r”) print(file_obj.read()) except OSError as e: print(f"File error: {e}") finally: if ‘file_obj’ in locals(): file_obj.close() elif ‘fd’ in locals(): os.close(fd)
The first part shows handling invalid descriptors. The second demonstrates a complete safe approach with proper cleanup in finally block.
Always ensure descriptors and file objects are properly closed, even when errors occur.
os.fdopen is commonly used with pipes created by os.pipe(). This example shows inter-process communication using pipe descriptors.
pipe_communication.py
import os
read_fd, write_fd = os.pipe()
reader = os.fdopen(read_fd, “r”) writer = os.fdopen(write_fd, “w”)
writer.write(“Message through pipe\n”) writer.flush() # Ensure data is sent
message = reader.readline() print(f"Received: {message.strip()}")
writer.close() reader.close()
The example creates a pipe, converts its descriptors to file objects, then demonstrates simple communication. Pipes are unidirectional - one for reading, one for writing.
This pattern is fundamental for inter-process communication in Python.
This advanced example shows how to create non-blocking file objects from descriptors using os.fdopen after setting O_NONBLOCK flag.
non_blocking.py
import os import errno import fcntl
fd = os.open(“fifo”, os.O_RDONLY | os.O_NONBLOCK)
f = os.fdopen(fd, “r”)
try: data = f.read() print(f"Read: {data}") except IOError as e: if e.errno == errno.EAGAIN: print(“No data available (non-blocking)”) else: print(f"Read error: {e}")
f.close()
The example demonstrates handling non-blocking I/O operations where reads may fail with EAGAIN when no data is available immediately.
Non-blocking mode is useful for event-driven programs that can’t afford to block on I/O operations.
Descriptor leaks: Always close file objects/descriptors
Race conditions: File state may change after opening
Privilege separation: Descriptors may bypass permission checks
Resource limits: Too many open descriptors can crash program
Platform differences: Behavior may vary between OSes
Use context managers: with statements for automatic cleanup
Prefer high-level APIs: Use open() when possible
Check return values: Verify descriptor/file object creation
Document ownership: Clearly note who closes descriptors
Limit scope: Keep descriptors/file objects in smallest scope
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.