Python stat: File Types and Permissions

Published on: August 10, 2026
Reading time: 5 minutes
Locked folder representing file types and permissions with Python stat

The stat module in Python’s standard library interprets metadata returned by os.stat(), os.fstat(), and os.lstat(). That metadata includes object type, permissions, owner identifiers, size, link count, device, inode, and timestamps. The module is particularly useful when a program needs to evaluate several attributes without issuing repeated system calls.

This guide explains how to recognize regular files, directories, links, and special objects; extract permission bits; display readable modes; understand platform differences; and avoid security problems such as symbolic-link confusion and time-of-check/time-of-use races.

Reading metadata with os.stat

import os

info = os.stat("data.txt")
print(info.st_size)
print(info.st_mtime)
print(info.st_mode)

The result behaves like a tuple but provides named attributes. Prefer the named form over historical indexes such as ST_SIZE, because it is easier to read and maintain.

Identifying the object type

The st_mode field combines object type and permission bits. Functions including S_ISREG(), S_ISDIR(), S_ISLNK(), S_ISSOCK(), S_ISFIFO(), S_ISCHR(), and S_ISBLK() test specific types.

import os
import stat

mode = os.lstat("shortcut").st_mode

if stat.S_ISLNK(mode):
    print("symbolic link")
elif stat.S_ISDIR(mode):
    print("directory")
elif stat.S_ISREG(mode):
    print("regular file")

Use lstat() when you need information about the link itself. stat() normally follows a symbolic link and describes its target.

Avoiding repeated system calls

Helpers such as os.path.isfile() and os.path.isdir() are convenient, but every test may require another filesystem query. When a program already has a stat result, reuse st_mode.

info = os.stat(path)
mode = info.st_mode

regular = stat.S_ISREG(mode)
directory = stat.S_ISDIR(mode)
permissions = stat.S_IMODE(mode)

This pattern is useful in indexers, backup tools, scanners, file browsers, and inventory jobs that process thousands of entries.

Displaying a readable mode

stat.filemode() converts a mode to text similar to ls -l, such as -rw-r--r-- or drwxr-xr-x.

import os
import stat

info = os.stat("data.txt")
print(stat.filemode(info.st_mode))

The first character describes the type. The remaining nine represent read, write, and execute bits for owner, group, and others. The string is excellent for logs and interfaces, but access decisions should rely on real APIs and policies.

Extracting configurable permission bits

S_IMODE() removes the file-type portion and retains permissions plus sticky, set-user-ID, and set-group-ID bits where supported.

current = os.stat("script.sh").st_mode
permissions = stat.S_IMODE(current)
print(oct(permissions))

When calling os.chmod(), use deliberate masks. Do not blindly copy modes from untrusted files, especially special privilege bits.

Owner, group, and other permissions

The module defines constants such as S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, and S_IXOTH.

mode = os.stat("file.txt").st_mode

if mode & stat.S_IWOTH:
    print("world writable")
if mode & stat.S_IXUSR:
    print("owner executable")

These bits describe the configured mode. They do not guarantee that the current process can access the object. ACLs, privileges, read-only mounts, sandboxing, and security frameworks also matter.

Sticky, setuid, and setgid

S_ISVTX represents the sticky bit. On directories such as /tmp, it restricts deletion and renaming. S_ISUID and S_ISGID have special Unix meanings.

Auditing tools may report these bits, but should not clear or add them automatically without a documented policy. A naive remediation can break software or introduce a vulnerability.

File size is type-dependent

For regular files, st_size is the byte length. For FIFOs and sockets on some Unix systems, it may report bytes waiting to be read. Device semantics vary further.

Before using the value to allocate memory or validate an upload, confirm that S_ISREG(st_mode) is true and enforce an independent maximum.

atime, mtime, and ctime

st_atime records last access, st_mtime records content modification, and st_ctime is platform-dependent. On Unix it generally represents the last metadata change; on Windows it has traditionally represented creation time.

Do not interpret ctime as portable creation time. Filesystems, mount options, and timestamp precision differ. For exact comparisons, prefer nanosecond fields such as st_mtime_ns.

st_ino and st_dev help identify an object on compatible systems. st_nlink reports the number of hard links.

info = os.stat("data.txt")
identity = (info.st_dev, info.st_ino)
print(identity, info.st_nlink)

The pair can help avoid processing the same inode twice during a scan, but it should not be persisted as a permanent global identifier because inode numbers may be reused.

A path can change between validation and use. Another process may replace a checked file, producing a TOCTOU race. For sensitive operations, prefer descriptor-based APIs, dir_fd, follow_symlinks=False, and secure open flags provided by the operating system.

Also validate the approved root and do not rely only on a previous file-type or extension check.

BSD and macOS flags

On compatible platforms, the module exposes flags such as UF_IMMUTABLE, UF_APPEND, UF_HIDDEN, and several SF_* values. Python 3.13 expanded some definitions.

Check availability with hasattr(stat, "UF_IMMUTABLE"). A constant’s presence does not guarantee identical behavior on every filesystem.

Windows file attributes

On Windows, stat results may include st_file_attributes and st_reparse_tag. Constants include FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_REPARSE_POINT, and known tags for links and mount points.

info = os.stat(path, follow_symlinks=False)
attributes = getattr(info, "st_file_attributes", 0)

if attributes & stat.FILE_ATTRIBUTE_HIDDEN:
    print("hidden on Windows")

Using getattr() keeps code importable on other platforms.

A practical inventory function

from pathlib import Path
import os
import stat


def describe(path: Path):
    info = os.lstat(path)
    mode = info.st_mode
    if stat.S_ISLNK(mode):
        kind = "link"
    elif stat.S_ISDIR(mode):
        kind = "directory"
    elif stat.S_ISREG(mode):
        kind = "file"
    else:
        kind = "special"
    return {
        "name": path.name,
        "kind": kind,
        "mode": stat.filemode(mode),
        "size": info.st_size,
        "mtime_ns": info.st_mtime_ns,
    }

The function uses lstat() so links are not followed. A complete application must still handle permission errors, disappearing files, and traversal limits.

Common mistakes

  • Treating st_ctime as creation time everywhere.
  • Calling stat() when the link itself should be inspected.
  • Trusting st_size without checking the object type.
  • Treating mode bits as a complete authorization answer.
  • Repeating filesystem queries unnecessarily.
  • Ignoring races between checking and using a path.
  • Assuming platform-specific flags are universally available.
  • Reuse one stat result for multiple tests.
  • Use S_IS* helpers for types.
  • Use S_IMODE() for permission extraction.
  • Use filemode() for presentation only.
  • Prefer nanosecond timestamps for comparisons.
  • Define an explicit symbolic-link policy.
  • Test on every supported platform.

Continue with Python filecmp, Python mmap, Python platform, Python sysconfig, and Python fnmatch.

See the official stat documentation and the os.stat documentation.

Conclusion

The stat module turns low-level modes and attributes into readable, portable tests. It is valuable for audits, backups, indexing, and system tools, but it must be combined with error handling, safe path policies, and APIs designed to resist race conditions.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Developer working with UTC timestamps and Python calendar.timegm
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    calendar.timegm: Convert UTC to Unix Timestamps

    Learn Python calendar.timegm to convert UTC date tuples into Unix timestamps safely and avoid local-time conversion bugs.

    Ler mais

    Tempo de leitura: 5 minutos
    14/09/2026
    Programmer analyzing code to identify file MIME types with Python
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    mimetypes.guess_file_type: Detect MIME Types

    Learn Python mimetypes.guess_file_type for MIME detection in paths, URLs, uploads, and HTTP responses with safe fallbacks.

    Ler mais

    Tempo de leitura: 5 minutos
    13/09/2026
    Server components representing isolated Python interpreters running in parallel
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    InterpreterPoolExecutor: True Parallelism in Python

    Learn Python InterpreterPoolExecutor for CPU-bound tasks, isolated interpreters, true parallelism, and safer concurrency design.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Microprocessor representing CPUs available to a Python process
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    os.process_cpu_count: Count Available CPUs

    Learn Python os.process_cpu_count to size workers according to the CPUs actually available to the process.

    Ler mais

    Tempo de leitura: 4 minutos
    12/09/2026
    Laptop with digital code representing SQLite BLOB data
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    sqlite3.Blob: Incremental BLOB Reads and Writes

    Learn Python sqlite3.Blob for incremental BLOB reads and writes, lower memory use, and safer binary data handling in SQLite.

    Ler mais

    Tempo de leitura: 5 minutos
    12/09/2026
    Statistical analysis for Python random.binomialvariate
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    random.binomialvariate: Simulate Binomial Outcomes

    Learn Python random.binomialvariate to simulate successes, validate probabilities, and analyze binomial scenarios with practical examples.

    Ler mais

    Tempo de leitura: 5 minutos
    11/09/2026