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

    Laptop with code representing automatic documentation with Python pydoc
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python pydoc: Automatic Documentation

    Learn Python pydoc to generate terminal help, HTML, search, and a local documentation server safely from docstrings.

    Ler mais

    Tempo de leitura: 6 minutos
    10/08/2026
    International keyboard representing numbers, currency, and dates with Python locale
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python locale: Numbers, Currency, and Dates

    Learn Python locale to format and parse numbers, currency, dates, encodings, and cultural sorting without concurrency mistakes.

    Ler mais

    Tempo de leitura: 6 minutos
    09/08/2026
    Monitor and network representing system information with Python platform
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python platform: System Information

    Learn Python platform to identify operating systems, architecture, Linux distributions, Python versions, and runtime environments safely.

    Ler mais

    Tempo de leitura: 6 minutos
    09/08/2026
    Source code and compiler representing build paths and variables with Python sysconfig
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python sysconfig: Paths and Build Info

    Learn Python sysconfig to discover installation paths, build variables, headers, virtual environments, and platform tags safely.

    Ler mais

    Tempo de leitura: 7 minutos
    09/08/2026
    Hard drive representing memory-mapped files with Python mmap
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python mmap: Memory-Mapped Files

    Learn Python mmap to map files into memory, search bytes, share data, and choose read, write, or copy-on-write access safely.

    Ler mais

    Tempo de leitura: 6 minutos
    09/08/2026
    Source code representing parser tokens and constants with the Python token module
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python token: Parser Constants

    Learn Python token constants for lexical types, exact operators, indentation, f-strings, t-strings, and version-aware parsers.

    Ler mais

    Tempo de leitura: 7 minutos
    07/08/2026