os.access

mode is F_OK (exists) or an OR of R_OK, W_OK and X_OK. access never raises for a missing path - it returns False. It checks with the REAL user id, and the answer can be stale a moment later, so for opening files the docs recommend just trying (EAFP). Available on Unix and Windows; on Windows it only sees existence and the read-only flag.

os functionPython 3 (all) (dir_fd, effective_ids, follow_symlinks 3.3+)
Common call
os.access(path, os.R_OK | os.W_OK)
Returns
True / False (never raises for missing paths)
Replaces
Parsing st_mode bits by hand
Watch out
Combine flags with |, not and
os.access(pathpath — The file or directory to test.type: str | bytes | PathLike · required, modemode — os.F_OK (0) to test existence, or R_OK (4), W_OK (2), X_OK (1) OR-ed together.type: int · required, *, dir_fddir_fd — Resolve a relative path against this open directory (Unix).type: int · default: None=None, effective_idseffective_ids — Check with the effective uid/gid instead of the real ones; NotImplementedError where os.supports_effective_ids does not contain os.access.type: bool · default: False=False, follow_symlinksfollow_symlinks — False tests a symlink itself.type: bool · default: True=True)
→ bool

Parameters

NameTypeRequiredDescription
pathstr | bytes | PathLikeyesThe file or directory to test.
modeintyesos.F_OK (0) to test existence, or R_OK (4), W_OK (2), X_OK (1) OR-ed together.
dir_fdintno (None)Resolve a relative path against this open directory (Unix).
effective_idsboolno (False)Check with the effective uid/gid instead of the real ones; NotImplementedError where os.supports_effective_ids does not contain os.access.
follow_symlinksboolno (True)False tests a symlink itself.

Return value

bool — True if every requested kind of access is allowed; False otherwise, including when the path does not exist.

Common patterns

EAFP instead of access + open
The pattern the docs recommend: try the operation and handle the error.
try:
    f = open('config.ini')
except FileNotFoundError:
    data = DEFAULTS
except PermissionError:
    raise SystemExit('config.ini is not readable')
else:
    with f:
        data = f.read()
Pre-flight check for a friendlier message
Fine for UI hints, as long as the real operation still handles errors.
import os
if not os.access(out_dir, os.W_OK):
    print(f'warning: cannot write to {out_dir}')
Is this executable? (Unix)
X_OK on a file; on a directory it means "may enter".
import os
is_exe = os.path.isfile(p) and os.access(p, os.X_OK)

Examples

1. F_OK: does it exist?
import os (os.access('.', os.F_OK), os.access('missing.txt', os.F_OK))
Returns
(True, False)
2. Read and write a new file
import os open('notes.txt', 'w').close() os.access('notes.txt', os.R_OK | os.W_OK)
Returns
True
3. A read-only file
import os open('ro.txt', 'w').close() os.chmod('ro.txt', 0o444) result = (os.access('ro.txt', os.R_OK), os.access('ro.txt', os.W_OK)) os.chmod('ro.txt', 0o644) result
Returns
(True, False)
4. Missing path: False, not an error
import os os.access('missing.txt', os.R_OK)
Returns
False
5. The flag values
import os (os.F_OK, os.R_OK, os.W_OK, os.X_OK)
Returns
(0, 4, 2, 1)
6. The EAFP alternative
try: with open('config.ini') as f: data = f.read() except FileNotFoundError: data = 'defaults' data
Returns
'defaults'

Pitfalls

1. Combining flags with and
`os.R_OK and os.W_OK` is just W_OK (and returns its second operand), so readability is never tested. Use the bitwise |.
and
import os
os.R_OK and os.W_OK
2
|
import os
os.R_OK | os.W_OK
6
2. Check, then open (a race)
Between access() and open() the file can be removed, replaced or have its permissions changed - the docs call this a security hole. Both versions print the same here; only the second one is safe when the file changes in between.
access then open
import os
if os.access('cfg.ini', os.R_OK):
    data = open('cfg.ini').read()
else:
    data = 'defaults'
data
'defaults'
try open
try:
    with open('cfg.ini') as f:
        data = f.read()
except OSError:
    data = 'defaults'
data
'defaults'

When to use

Use it
  • Friendly pre-flight messages ("output folder is not writable")
  • setuid programs that must check what the REAL user may do
  • Finding executables (X_OK) on Unix - or use shutil.which
Reach for something else
  • Guarding open() → just open it and catch OSError
  • Existence only → os.path.exists / Path.exists
  • Windows ACL checks → access only sees the read-only flag

Notes

CPython impl
access(2) / faccessat(2) on Unix; on Windows GetFileAttributesW: False when the path is missing, and False for W_OK only when the read-only attribute is set on a file
Real vs effective ids
access() uses the real uid/gid (the setuid use case). effective_ids=True switches to the effective ids where supported
Windows
X_OK is True for any existing file (verified: a fresh .txt file reports X_OK True on Windows and False on Linux)
root
On Linux root gets W_OK True even for a 0o444 file (verified)
Network filesystems
The docs warn that I/O can still fail when access() says it would succeed

FAQ

What access() answers depends on the permissions of the machine and on who runs the code (root, for example, gets True for writing almost anything), so a live result would describe our server, not your files. The examples create their own files and only show answers that are the same on Linux and Windows.