Complete guide to Python's os.chmod function covering file permission changes, mode bits, and practical examples.
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.
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.
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”
if not os.path.exists(file_path): with open(file_path, “w”) as f: f.write(“Sample content”)
os.chmod(file_path, 0o644) print(f"Set permissions for {file_path} to 644 (rw-r–r–)")
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.
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”
with open(file_path, “w”) as f: f.write("#!/bin/bash\necho ‘Hello World’")
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.
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”
if not os.path.exists(dir_path): os.mkdir(dir_path)
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:]}")
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.
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”
with open(file_path, “w”) as f: f.write(“key=value”)
current_mode = os.stat(file_path).st_mode
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:]}")
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.
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…”)
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.
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”)
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”)
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.
Least privilege: Grant only necessary permissions
Race conditions: Avoid TOCTOU issues with sensitive files
Umask interaction: New files inherit umask-modified permissions
Symbolic links: chmod follows symlinks by default
Windows limitations: Permission support varies by platform
Use octal carefully: Leading zeros matter (0o755 not 0755)
Prefer stat constants: More readable than magic numbers
Document changes: Comment why permissions are being modified
Test thoroughly: Verify permissions after changes
Consider alternatives: For complex ACLs, use platform tools
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.