ZetCode

Python os.close Function

Last modified April 11, 2025

This comprehensive guide explores Python's os.close function, which closes file descriptors. We'll cover proper resource management, error handling, and practical file operations examples.

Basic Definitions

The os.close function closes a file descriptor, making it available for reuse. It's a low-level operation that releases system resources.

Key parameter: fd (file descriptor to close). Raises OSError if the descriptor is invalid. Returns None on success.

Basic File Descriptor Closing

This example demonstrates opening a file and properly closing its descriptor using os.close. Always close descriptors to avoid leaks.

basic_close.py
import os

# Open a file and get its descriptor
fd = os.open("example.txt", os.O_RDWR | os.O_CREAT)

try:
    # Work with the file descriptor
    os.write(fd, b"Hello World")
finally:
    # Ensure descriptor is closed
    os.close(fd)
    print("File descriptor closed")

# Verify descriptor is closed
try:
    os.write(fd, b"More data")
except OSError as e:
    print(f"Error: {e}")

The example shows proper resource management with try/finally. The finally block ensures the descriptor is closed even if an error occurs.

Attempting to use a closed descriptor raises OSError. This confirms the descriptor was properly closed.

Closing Multiple Descriptors

When working with multiple files, each descriptor must be closed individually. This example shows proper handling of multiple resources.

multiple_close.py
import os

# Open multiple files
fd1 = os.open("file1.txt", os.O_RDWR | os.O_CREAT)
fd2 = os.open("file2.txt", os.O_RDWR | os.O_CREAT)
fd3 = os.open("file3.txt", os.O_RDWR | os.O_CREAT)

try:
    # Work with files
    os.write(fd1, b"Data for file1")
    os.write(fd2, b"Data for file2")
    os.write(fd3, b"Data for file3")
finally:
    # Close all descriptors
    for fd in [fd1, fd2, fd3]:
        try:
            os.close(fd)
            print(f"Closed descriptor {fd}")
        except OSError as e:
            print(f"Error closing {fd}: {e}")

This pattern ensures all descriptors are closed properly, even if some operations fail. Each close is wrapped in its own try/except.

The loop handles closing multiple descriptors efficiently while maintaining error handling for each operation.

Context Manager Alternative

Python's context managers provide a cleaner alternative to manual os.close calls. This example compares both approaches.

context_manager.py
import os
from contextlib import contextmanager

# Manual approach
fd = os.open("manual.txt", os.O_RDWR | os.O_CREAT)
try:
    os.write(fd, b"Manual management")
finally:
    os.close(fd)

# Context manager approach
@contextmanager
def open_fd(path, flags):
    fd = os.open(path, flags)
    try:
        yield fd
    finally:
        os.close(fd)

with open_fd("context.txt", os.O_RDWR | os.O_CREAT) as fd:
    os.write(fd, b"Context manager")

# Built-in open() is preferred for regular files
with open("regular.txt", "w") as f:
    f.write("High-level interface")

The context manager approach encapsulates the open/close pattern, making code cleaner and less error-prone.

For most file operations, Python's built-in open() is preferred over low-level descriptor management.

Error Handling

os.close can raise OSError for invalid descriptors. This example demonstrates proper error handling patterns.

error_handling.py
import os

def safe_close(fd):
    """Safely close a file descriptor with error handling."""
    try:
        os.close(fd)
        print(f"Successfully closed descriptor {fd}")
    except OSError as e:
        print(f"Error closing descriptor {fd}: {e}")

# Valid descriptor
fd1 = os.open("valid.txt", os.O_RDWR | os.O_CREAT)
safe_close(fd1)

# Already closed descriptor
safe_close(fd1)

# Invalid descriptor
safe_close(9999)

# Double close protection
fd2 = os.open("another.txt", os.O_RDWR | os.O_CREAT)
safe_close(fd2)
fd2 = None  # Prevent accidental reuse

The safe_close function provides robust error handling for descriptor closing operations.

Setting descriptors to None after closing helps prevent accidental reuse of closed descriptors.

Pipe Communication

os.close is crucial when working with pipes. This example shows proper pipe cleanup in inter-process communication.

pipe_example.py
import os

# Create a pipe
r, w = os.pipe()

pid = os.fork()

if pid == 0:
    # Child process
    os.close(r)  # Close unused read end
    os.write(w, b"Hello from child")
    os.close(w)
    os._exit(0)
else:
    # Parent process
    os.close(w)  # Close unused write end
    data = os.read(r, 100)
    os.close(r)
    print(f"Received: {data.decode()}")
    os.waitpid(pid, 0)

Pipes require closing unused ends in both processes to prevent hangs and resource leaks.

The example shows proper cleanup in both parent and child processes for correct pipe operation.

File Descriptor Inheritance

This example demonstrates how file descriptors behave across process boundaries and the importance of proper closing.

inheritance_example.py
import os

# Open file in parent
fd = os.open("inherited.txt", os.O_RDWR | os.O_CREAT)
os.write(fd, b"Parent data")

pid = os.fork()

if pid == 0:
    # Child process
    print(f"Child can access descriptor {fd}")
    os.lseek(fd, 0, os.SEEK_SET)
    data = os.read(fd, 100)
    print(f"Child read: {data.decode()}")
    
    # Close in child (doesn't affect parent)
    os.close(fd)
    os._exit(0)
else:
    # Parent process
    os.waitpid(pid, 0)
    print("Child process finished")
    
    # Parent can still use descriptor
    os.write(fd, b"\nMore parent data")
    os.close(fd)  # Final close

File descriptors are inherited by child processes. Each process must close them independently.

Closing in the child doesn't affect the parent's copy of the descriptor. Both should close their copies.

Security Considerations

Best Practices

Source References

Author

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.