Python os.get_inheritable Function
Last modified April 11, 2025
This comprehensive guide explores Python's os.get_inheritable
function,
which checks if a file descriptor can be inherited by child processes. We'll cover
file descriptor flags, inheritance behavior, and practical examples.
Basic Definitions
The os.get_inheritable
function checks if a file descriptor is
marked as inheritable. Inheritable descriptors are passed to child processes.
Key parameter: fd (file descriptor to check). Returns True if the descriptor is inheritable, False otherwise. Available in Python 3.4+.
Checking Standard Stream Inheritance
Standard streams (stdin, stdout, stderr) are typically inheritable. This example checks their inheritance status.
import os import sys # Check standard streams print(f"stdin inheritable: {os.get_inheritable(sys.stdin.fileno())}") print(f"stdout inheritable: {os.get_inheritable(sys.stdout.fileno())}") print(f"stderr inheritable: {os.get_inheritable(sys.stderr.fileno())}") # Check a regular file with open("test.txt", "w") as f: print(f"Regular file inheritable: {os.get_inheritable(f.fileno())}")
This example first checks the standard streams, which usually return True. Then it checks a newly opened file, which typically inherits the default inheritance setting.
The results may vary based on platform and how the Python process was launched.
Modifying Inheritance Flag
We can change inheritance status with os.set_inheritable
and
verify changes with os.get_inheritable
.
import os # Create a temporary file with open("temp.txt", "w") as f: fd = f.fileno() print(f"Original inheritable: {os.get_inheritable(fd)}") # Disable inheritance os.set_inheritable(fd, False) print(f"After disabling: {os.get_inheritable(fd)}") # Re-enable inheritance os.set_inheritable(fd, True) print(f"After re-enabling: {os.get_inheritable(fd)}")
This demonstrates changing a file descriptor's inheritance flag and verifying the changes. The file descriptor remains valid after modification.
Note that changing inheritance affects only new child processes, not existing ones.
Checking Pipe Inheritance
Pipes created with os.pipe
can be checked for inheritance.
This example shows both read and write ends of a pipe.
import os # Create a pipe r, w = os.pipe() print(f"Read end inheritable: {os.get_inheritable(r)}") print(f"Write end inheritable: {os.get_inheritable(w)}") # Modify one end os.set_inheritable(r, False) print(f"Modified read end: {os.get_inheritable(r)}") # Clean up os.close(r) os.close(w)
This creates a pipe and checks both ends' inheritance status. By default, both ends are typically inheritable. We then modify one end's status.
Always remember to close file descriptors when they're no longer needed.
Inheritance in Subprocesses
This example demonstrates how inheritance affects actual subprocess behavior. We'll create a file and check its visibility in a child process.
import os import subprocess # Create a test file with open("shared.txt", "w") as f: fd = f.fileno() print(f"Before change: {os.get_inheritable(fd)}") # Test with inheritable descriptor subprocess.run(["python", "-c", "import os; print(os.path.exists('shared.txt'))"]) # Make non-inheritable and test again os.set_inheritable(fd, False) print(f"After change: {os.get_inheritable(fd)}") subprocess.run(["python", "-c", "import os; print(os.path.exists('shared.txt'))"])
The first subprocess can access the file because the descriptor is inheritable. After disabling inheritance, the file remains accessible through the filesystem.
This shows the difference between descriptor inheritance and file path access.
Comparing with fcntl Module
We can compare os.get_inheritable
with the fcntl
module's approach to checking descriptor flags.
import os import fcntl with open("compare.txt", "w") as f: fd = f.fileno() # Using os.get_inheritable print(f"os.get_inheritable: {os.get_inheritable(fd)}") # Using fcntl flags = fcntl.fcntl(fd, fcntl.F_GETFD) print(f"fcntl FD_CLOEXEC: {not bool(flags & fcntl.FD_CLOEXEC)}") # Make them differ os.set_inheritable(fd, False) print("\nAfter change:") print(f"os.get_inheritable: {os.get_inheritable(fd)}") flags = fcntl.fcntl(fd, fcntl.F_GETFD) print(f"fcntl FD_CLOEXEC: {not bool(flags & fcntl.FD_CLOEXEC)}")
This shows two ways to check inheritance status. os.get_inheritable
is simpler, while fcntl
provides more control.
Note that FD_CLOEXEC is the inverse of inheritable - when set, the descriptor is not inheritable.
Platform Differences
This example demonstrates platform-specific behavior of os.get_inheritable
between Unix and Windows systems.
import os import sys def check_inheritance(fd): try: return os.get_inheritable(fd) except (AttributeError, OSError) as e: return f"Error: {e}" # Check standard streams print("Platform:", sys.platform) print(f"stdin: {check_inheritance(sys.stdin.fileno())}") print(f"stdout: {check_inheritance(sys.stdout.fileno())}") print(f"stderr: {check_inheritance(sys.stderr.fileno())}") # Check invalid descriptor print(f"Invalid fd: {check_inheritance(9999)}")
This code checks inheritance on different platforms and handles potential errors. Windows may behave differently from Unix-like systems for certain descriptors.
The example also shows error handling for invalid file descriptors.
Security Considerations
- Descriptor leaks: Inheritable descriptors can leak to child processes
- Privilege escalation: Careless inheritance can create security holes
- Default settings: Understand your platform's default inheritance
- Cleanup: Close unnecessary descriptors before process creation
- Platform differences: Behavior varies between operating systems
Best Practices
- Explicit control: Set inheritance flags deliberately
- Minimize inheritance: Only pass needed descriptors
- Use context managers: Ensure proper cleanup of resources
- Check documentation: Understand platform-specific behavior
- Combine with subprocess: Use pass_fds parameter when appropriate
Source References
Author
List all Python tutorials.