Python os.stat Function
Last modified April 11, 2025
This comprehensive guide explores Python's os.stat
function,
which retrieves detailed file system metadata. We'll cover stat structure
attributes, platform differences, and practical file information examples.
Basic Definitions
The os.stat
function returns a stat_result object containing
file system metadata. It works on both files and directories across platforms.
Key attributes include st_mode (permissions), st_size (size), st_mtime (modification time). The exact attributes available vary by operating system.
Getting Basic File Information
The simplest use of os.stat
retrieves basic file metadata like
size and modification time. This example shows common attributes available.
import os import time file_path = "example.txt" stats = os.stat(file_path) print(f"File: {file_path}") print(f"Size: {stats.st_size} bytes") print(f"Last modified: {time.ctime(stats.st_mtime)}") print(f"Last accessed: {time.ctime(stats.st_atime)}") print(f"Created: {time.ctime(stats.st_ctime)}") print(f"Mode: {oct(stats.st_mode)}") print(f"Inode: {stats.st_ino}")
This code retrieves and displays common file attributes. Note st_ctime means creation time on Windows but metadata change time on Unix-like systems.
The st_mode attribute shows file permissions and type in octal format. We'll explore this in more detail in later examples.
Checking File Type
The st_mode attribute contains file type information that can be checked using stat module constants. This helps distinguish files from directories.
import os import stat path = "example.txt" stats = os.stat(path) mode = stats.st_mode if stat.S_ISREG(mode): print(f"{path} is a regular file") elif stat.S_ISDIR(mode): print(f"{path} is a directory") elif stat.S_ISLNK(mode): print(f"{path} is a symbolic link") elif stat.S_ISSOCK(mode): print(f"{path} is a socket") else: print(f"{path} is of unknown type")
This example uses stat module functions to determine the file type. Each function checks specific bits in the st_mode field for the file type.
Note that for symbolic links, os.stat follows them by default. Use os.lstat to get info about the link itself rather than its target.
Checking File Permissions
File permissions can be extracted from st_mode using bitwise operations or the stat module constants. This example shows both approaches.
import os import stat file_path = "script.sh" stats = os.stat(file_path) mode = stats.st_mode # Using bitwise operations user_read = mode & stat.S_IRUSR user_write = mode & stat.S_IWUSR user_exec = mode & stat.S_IXUSR print(f"Owner permissions:") print(f" Read: {'Yes' if user_read else 'No'}") print(f" Write: {'Yes' if user_write else 'No'}") print(f" Execute: {'Yes' if user_exec else 'No'}") # Using stat module constants print("\nUsing stat module:") print(f"Readable by owner: {bool(mode & stat.S_IRUSR)}") print(f"Writable by owner: {bool(mode & stat.S_IWUSR)}") print(f"Executable by owner: {bool(mode & stat.S_IXUSR)}")
The first approach uses bitwise AND to check specific permission bits. The second shows a more concise version using stat constants directly.
Similar checks can be done for group (S_IRGRP etc.) and others (S_IROTH etc.) permissions following the same pattern.
Working with File Times
os.stat provides three time-related attributes: st_atime (access), st_mtime (modification), and st_ctime (creation/metadata change). This example shows how to work with them.
import os import time from datetime import datetime file_path = "document.txt" stats = os.stat(file_path) # Raw timestamp values print(f"Access time (timestamp): {stats.st_atime}") print(f"Modification time (timestamp): {stats.st_mtime}") print(f"Metadata change time (timestamp): {stats.st_ctime}") # Convert to readable format print("\nFormatted times:") print(f"Last accessed: {datetime.fromtimestamp(stats.st_atime)}") print(f"Last modified: {datetime.fromtimestamp(stats.st_mtime)}") print(f"Metadata changed: {datetime.fromtimestamp(stats.st_ctime)}") # Time comparisons now = time.time() hours_since_mod = (now - stats.st_mtime) / 3600 print(f"\nModified {hours_since_mod:.1f} hours ago")
This demonstrates accessing raw timestamp values and converting them to human-readable formats. It also shows calculating time differences.
Remember that st_ctime has different meanings across platforms - creation time on Windows, metadata change time on Unix-like systems.
Comparing Files
The st_ino (inode) and st_dev (device) attributes can uniquely identify files and detect hard links. This example shows file comparison techniques.
import os def are_same_file(path1, path2): stats1 = os.stat(path1) stats2 = os.stat(path2) return (stats1.st_ino == stats2.st_ino and stats1.st_dev == stats2.st_dev) file1 = "original.txt" file2 = "hardlink.txt" file3 = "copy.txt" print(f"{file1} and {file2} same file: {are_same_file(file1, file2)}") print(f"{file1} and {file3} same file: {are_same_file(file1, file3)}") # Additional comparison by metadata stats1 = os.stat(file1) stats2 = os.stat(file3) if (stats1.st_size == stats2.st_size and stats1.st_mtime == stats2.st_mtime): print("\nFiles have same size and modification time") else: print("\nFiles differ in size or modification time")
The are_same_file function checks if two paths refer to the same physical file by comparing inode and device numbers. This detects hard links.
The second comparison shows how to check if files have identical metadata without necessarily being the same physical file.
Handling Symbolic Links
os.stat follows symbolic links by default. Use os.lstat to get information about the link itself rather than its target.
import os import stat # Create a symbolic link for demonstration if not os.path.exists("target.txt"): with open("target.txt", "w") as f: f.write("Target file content") if not os.path.exists("link.txt"): os.symlink("target.txt", "link.txt") # Regular stat follows symlinks target_stats = os.stat("link.txt") print(f"Target file size: {target_stats.st_size} bytes") # lstat gets info about the link itself link_stats = os.lstat("link.txt") print(f"\nLink info:") print(f"Size: {link_stats.st_size} bytes") print(f"Is symlink: {stat.S_ISLNK(link_stats.st_mode)}") # Compare the two print("\nComparison:") print(f"Same inode: {target_stats.st_ino == link_stats.st_ino}") print(f"Same device: {target_stats.st_dev == link_stats.st_dev}")
This example demonstrates the difference between os.stat and os.lstat when working with symbolic links. The link size reported is the length of the path it contains.
Note that os.lstat is particularly useful when you need to detect and handle symbolic links specially in your application.
Platform-Specific Attributes
Some stat attributes are platform-specific. This example shows how to safely access them with fallback behavior when unavailable.
import os import platform import sys file_path = sys.executable # Using Python executable as example stats = os.stat(file_path) print(f"System: {platform.system()}") print(f"File: {file_path}") # Common attributes print(f"\nCommon attributes:") print(f"Size: {stats.st_size} bytes") print(f"Mode: {oct(stats.st_mode)}") # Platform-specific attributes print("\nPlatform-specific attributes:") if hasattr(stats, 'st_file_attributes'): # Windows print(f"File attributes: {stats.st_file_attributes}") if hasattr(stats, 'st_birthtime'): # macOS and some Unix print(f"Birth time: {stats.st_birthtime}") elif hasattr(stats, 'st_ctime'): # Fallback print(f"Creation time: {stats.st_ctime}") if hasattr(stats, 'st_blksize'): # Unix block size print(f"Block size: {stats.st_blksize}") print(f"Blocks: {stats.st_blocks}")
This code demonstrates how to safely check for platform-specific attributes using hasattr. It shows different attributes available on various systems.
The example also highlights the importance of writing cross-platform code when working with file system metadata.
Security Considerations
- TOCTOU risks: File state can change between stat and use
- Symbolic links: os.stat follows them by default
- Permission checks: Verify before sensitive operations
- Error handling: Always handle FileNotFoundError
- Platform differences: Attribute availability varies
Best Practices
- Use os.lstat: When you need info about symlinks themselves
- Check attribute existence: With hasattr for cross-platform code
- Handle exceptions: Especially FileNotFoundError
- Prefer pathlib: For simpler path operations in new code
- Document assumptions: About file types and permissions
Source References
Author
List all Python tutorials.