Complete guide to Python's os.umask function covering permission masks, file creation modes, and practical examples.
Last modified April 11, 2025
This comprehensive guide explores Python’s os.umask function, which sets the file mode creation mask. We’ll cover permission bits, mask calculation, and practical file creation examples.
The os.umask function sets the current numeric umask value and returns the previous value. The umask determines default file permissions.
The umask is subtracted from the default permissions (usually 666 for files, 777 for directories) to determine actual permissions. It uses octal notation.
To retrieve the current umask without changing it, call os.umask with the current value. This preserves the existing mask while reading it.
get_umask.py
import os
current_umask = os.umask(0) os.umask(current_umask) # Restore original umask
print(f"Current umask: {oct(current_umask)}") print(f"Interpreted as: {current_umask:03o}")
print("\nCommon umask values:") print(“022 - Owner has full, group/others read/execute”) print(“002 - Owner/group have full, others read/execute”) print(“077 - Only owner has full access”)
This example shows how to safely read the current umask value. The octal format represents which permission bits will be disabled by default.
The umask is process-specific and affects all subsequent file/directory creations within that process.
To change the umask, pass the new value to os.umask. The function returns the previous umask value. Use octal notation for clarity.
set_umask.py
import os
original_umask = os.umask(0) os.umask(original_umask) # Restore immediately
print(f"Original umask: {oct(original_umask)}")
new_umask = 0o077 old_umask = os.umask(new_umask) print(f"Changed umask from {oct(old_umask)} to {oct(new_umask)}")
with open(“restricted_file.txt”, “w”) as f: f.write(“Secret content”)
os.umask(original_umask) print(f"Restored original umask: {oct(original_umask)}")
This demonstrates changing the umask temporarily to create a file with restrictive permissions, then restoring the original umask.
The umask affects all subsequent file operations until changed again or the process ends.
The umask works by subtracting permission bits from default modes. For files, default is 666 (rw-rw-rw-), for directories 777 (rwxrwxrwx).
umask_calculation.py
import os import stat
def show_permissions(path): mode = os.stat(path).st_mode print(f"{path}: {oct(mode & 0o777)}")
default_umask = 0o022 os.umask(default_umask)
with open(“normal_file.txt”, “w”) as f: f.write(“Test content”) os.mkdir(“normal_dir”)
show_permissions(“normal_file.txt”) # Should be 644 (666 - 022) show_permissions(“normal_dir”) # Should be 755 (777 - 022)
os.umask(0o077) with open(“private_file.txt”, “w”) as f: f.write(“Private content”) os.mkdir(“private_dir”)
show_permissions(“private_file.txt”) # Should be 600 (666 - 077) show_permissions(“private_dir”) # Should be 700 (777 - 077)
This example shows how different umask values affect file and directory permissions. The actual permissions are calculated by bitwise AND with the umask’s complement.
The umask doesn’t grant permissions - it only restricts the default ones.
For safe umask changes, use a context manager that automatically restores the original umask. This prevents accidental permission leaks.
umask_context.py
import os from contextlib import contextmanager
@contextmanager def temp_umask(new_umask): old_umask = os.umask(new_umask) try: yield finally: os.umask(old_umask)
print(f"Start umask: {oct(os.umask(0))}") os.umask(0o022) # Reset to common default
with temp_umask(0o077): print(f"Inside context umask: {oct(os.umask(0))}") os.umask(0o077) # Restore within context with open(“temp_secure.txt”, “w”) as f: f.write(“Temporary secure file”)
print(f"After context umask: {oct(os.umask(0))}") os.umask(0o022) # Clean up
The context manager ensures the umask is always restored, even if an exception occurs. This is safer than manual umask management.
This pattern is especially useful in libraries where you can’t predict how your code will be used.
When creating directories recursively with os.makedirs, the umask affects all created directories. This example demonstrates the behavior.
makedirs_umask.py
import os import stat
def show_tree_permissions(root): for dirpath, dirnames, filenames in os.walk(root): print(f"\nDirectory: {dirpath}") mode = os.stat(dirpath).st_mode print(f"Permissions: {oct(mode & 0o777)}") for f in filenames: path = os.path.join(dirpath, f) mode = os.stat(path).st_mode print(f"File {f}: {oct(mode & 0o777)}")
os.umask(0o077)
try: os.makedirs(“secure_tree/sub1/sub2”) with open(“secure_tree/file1.txt”, “w”) as f: f.write(“Top level”) with open(“secure_tree/sub1/file2.txt”, “w”) as f: f.write(“Sub level”)
show_tree_permissions("secure_tree")
finally: # Clean up os.umask(0o022) # Remove test directories import shutil shutil.rmtree(“secure_tree”, ignore_errors=True)
All directories and files created under the restrictive umask inherit the limited permissions. The umask affects the entire operation.
This shows how umask provides a way to enforce permission policies throughout an application.
Umask behavior differs slightly between Unix and Windows systems. This example demonstrates the differences and how to handle them.
platform_umask.py
import os import sys import stat
def create_test_file(): test_file = “umask_test.txt” with open(test_file, “w”) as f: f.write(“Platform test”) return test_file
current_umask = os.umask(0) os.umask(current_umask)
print(f"Platform: {sys.platform}") print(f"Current umask: {oct(current_umask)}")
if sys.platform == “win32”: print("\nWindows notes:") print("- Umask affects only the execute bit") print("- Read/write permissions not fully enforced") print("- Default umask is usually 0o022")
# Windows ignores some umask bits
os.umask(0o077) # Try to set restrictive
test_file = create_test_file()
mode = os.stat(test_file).st_mode
print(f"Created file permissions: {oct(mode & 0o777)}")
os.unlink(test_file)
else: # Unix behavior print("\nUnix notes:") print("- Umask fully affects all permission bits") print("- Strict enforcement of permissions")
os.umask(0o077) # Restrictive umask
test_file = create_test_file()
mode = os.stat(test_file).st_mode
print(f"Created file permissions: {oct(mode & 0o777)}")
os.unlink(test_file)
os.umask(current_umask)
On Unix systems, the umask fully controls permissions. Windows has limited support, mainly affecting the execute bit.
When writing cross-platform code, don’t rely on umask alone for security. Add explicit permission checks.
Process-wide effect: Umask changes affect all subsequent file operations
Not thread-safe: Changing umask in threaded apps requires synchronization
Windows limitations: Umask has reduced effect on Windows systems
Temporary changes: Always restore original umask after temporary changes
Default values: Common umask values are 022 or 002 for shared systems
Use context managers: For temporary umask changes to ensure cleanup
Document umask usage: Clearly note where and why umask is changed
Combine with chmod: For precise control, use os.chmod after creation
Test permissions: Verify actual file permissions after creation
Consider defaults: Choose umask values appropriate for your security needs
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.