ZetCode

Python os.chmod Function

Last modified April 11, 2025

This comprehensive guide explores Python's os.chmod function, which changes file mode bits (permissions). We'll cover permission flags, symbolic vs numeric modes, and practical permission management examples.

Basic Definitions

The os.chmod function changes the mode (permissions) of a file or directory. It follows Unix-style permission bits and works on Windows too.

Key parameters: path (file/directory to modify), mode (permission flags: stat.S_IRWXU, stat.S_IRUSR, etc. or octal numbers like 0o755). Returns None.

Setting Basic File Permissions

This example demonstrates setting basic read, write, and execute permissions for a file using octal notation. Octal numbers represent permission bits.

basic_permissions.py
import os

file_path = "document.txt"

# Create file if it doesn't exist
if not os.path.exists(file_path):
    with open(file_path, "w") as f:
        f.write("Sample content")

# Set permissions to read/write for owner, read for others
os.chmod(file_path, 0o644)
print(f"Set permissions for {file_path} to 644 (rw-r--r--)")

# Verify permissions
mode = os.stat(file_path).st_mode
print(f"Current permissions: {oct(mode)[-3:]}")

This sets owner read/write (6), group read (4), and others read (4) permissions. The 0o prefix indicates octal notation in Python. The last print shows the mode.

Octal notation is compact but can be less readable than symbolic constants. The next examples will show alternative approaches.

Using stat Module Constants

The stat module provides symbolic constants for permission bits, making code more readable. This example combines them using bitwise OR operations.

stat_constants.py
import os
import stat

file_path = "script.sh"

# Create executable script
with open(file_path, "w") as f:
    f.write("#!/bin/bash\necho 'Hello World'")

# Set permissions: rwxr-xr-x
os.chmod(file_path, 
    stat.S_IRWXU |  # User read, write, execute
    stat.S_IRGRP |  # Group read
    stat.S_IXGRP |  # Group execute
    stat.S_IROTH |  # Others read
    stat.S_IXOTH    # Others execute
)

print(f"Made {file_path} executable for all")
print(f"Current permissions: {oct(os.stat(file_path).st_mode)[-3:]}")

This sets full permissions for owner (rwx), read/execute for group (r-x), and read/execute for others (r-x). The symbolic constants make the intent clear.

The stat constants can be combined flexibly to create any permission combination needed for your application.

Changing Directory Permissions

Directory permissions work similarly to files but with different meanings. Execute permission on directories allows traversing/searching them.

directory_permissions.py
import os
import stat

dir_path = "private_data"

# Create directory if it doesn't exist
if not os.path.exists(dir_path):
    os.mkdir(dir_path)

# Restrict directory to owner only (rwx------)
os.chmod(dir_path, stat.S_IRWXU)

print(f"Set strict permissions for {dir_path}")
print(f"Current permissions: {oct(os.stat(dir_path).st_mode)[-3:]}")

# Verify access
try:
    os.listdir(dir_path)
    print("Can access directory (running as owner)")
except PermissionError:
    print("Cannot access directory")

This sets directory permissions to rwx------ (700), allowing only the owner to access it. Other users can't list or enter the directory.

Directory permissions are crucial for security when storing sensitive data. They control who can access files within the directory.

Modifying Specific Permission Bits

You can modify specific permission bits while preserving others by first reading the current mode and then applying changes. This is more precise.

modify_bits.py
import os
import stat

file_path = "config.cfg"

# Create file with default permissions
with open(file_path, "w") as f:
    f.write("key=value")

# Get current permissions
current_mode = os.stat(file_path).st_mode

# Add group write permission
new_mode = current_mode | stat.S_IWGRP
os.chmod(file_path, new_mode)

print(f"Added group write permission to {file_path}")
print(f"New permissions: {oct(os.stat(file_path).st_mode)[-3:]}")

# Remove others read permission
new_mode = new_mode & ~stat.S_IROTH
os.chmod(file_path, new_mode)

print(f"Removed others read permission")
print(f"Final permissions: {oct(os.stat(file_path).st_mode)[-3:]}")

This first adds group write permission using bitwise OR, then removes others read using bitwise AND with the complement of the permission bit.

This approach is useful when you need to modify specific permissions without affecting other existing permission bits.

Handling Symbolic Permissions

While Python doesn't have built-in symbolic permission notation (like chmod u+x), we can create a helper function to parse such expressions.

symbolic_permissions.py
import os
import stat

def apply_symbolic_chmod(path, symbolic):
    """Apply symbolic permissions like chmod command"""
    current = os.stat(path).st_mode
    who, op, perms = symbolic[:1], symbolic[1:2], symbolic[2:]
    
    # Determine which bits to modify
    mask = 0
    if 'u' in who or 'a' in who:
        mask |= stat.S_IRWXU
    if 'g' in who or 'a' in who:
        mask |= stat.S_IRWXG
    if 'o' in who or 'a' in who:
        mask |= stat.S_IRWXO
    
    # Determine new permissions
    new_bits = 0
    if 'r' in perms:
        new_bits |= (stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
    if 'w' in perms:
        new_bits |= (stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
    if 'x' in perms:
        new_bits |= (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
    
    # Apply operation
    if op == '+':
        new_mode = current | (new_bits & mask)
    elif op == '-':
        new_mode = current & ~(new_bits & mask)
    elif op == '=':
        new_mode = (current & ~mask) | (new_bits & mask)
    
    os.chmod(path, new_mode)

file_path = "app.log"
with open(file_path, "w") as f:
    f.write("Log entries...")

# Examples of symbolic notation
apply_symbolic_chmod(file_path, "u=rw")  # User read/write
apply_symbolic_chmod(file_path, "go-r")  # Remove group/others read
apply_symbolic_chmod(file_path, "a+x")   # Add execute for all

print(f"Final permissions: {oct(os.stat(file_path).st_mode)[-3:]}")

This implements a basic symbolic permission parser similar to Unix chmod. It handles who (u/g/o/a), operation (+/-/=), and permissions (r/w/x).

While not as complete as the Unix command, it demonstrates how to implement symbolic permission changes in Python when needed.

Windows Compatibility Notes

On Windows, only certain permission bits are meaningful. This example shows what works and what doesn't on Windows systems.

windows_permissions.py
import os
import stat
import sys

file_path = "winfile.txt"

with open(file_path, "w") as f:
    f.write("Windows test")

# These work on Windows
os.chmod(file_path, stat.S_IREAD)   # Read-only
os.chmod(file_path, stat.S_IWRITE)  # Write-only
os.chmod(file_path, stat.S_IREAD | stat.S_IWRITE)  # Read/write

print("Basic permission changes work on Windows")

# These are ignored on Windows
try:
    os.chmod(file_path, stat.S_IXUSR)  # Execute
    print("Execute bit set (but ignored on Windows)")
except:
    print("Execute bit not supported")

if sys.platform == "win32":
    print("\nNote: Windows has limited permission support")
    print("Only read-only/writeable flags are effective")

Windows only respects the read-only flag (which corresponds to S_IREAD). Other permission bits may be stored but don't affect file access.

When writing cross-platform code, test permission changes on all target platforms to ensure consistent behavior.

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.