os.path

os.path is not one module but two: posixpath on Linux and macOS, ntpath on Windows. Same function names, different rules for separators and drives. Paths are plain strings in, plain strings out — most functions never touch the disk.

Files and directoriesPython 3.0+Live demo
Import
import os
import os.path
from os import path
import posixpath, ntpath
Really is
posixpath on Linux/macOS ('/' only), ntpath on Windows ('\' and '/', drive letters, UNC shares)
Pure string functions
join, split, splitext, basename, dirname, normpath, normcase, isabs, splitdrive, splitroot, commonpath, commonprefix
Ask the disk
exists, lexists, isfile, isdir, islink, ismount, isjunction, getsize, getmtime, getatime, getctime, samefile
Also depend on the machine
abspath and relpath (current directory), realpath (symlinks), expanduser and expandvars (environment)

Demo

Live evaluation
The folder, the file name and the extension of a POSIX path — the three functions you use most.
Try:
Inputs
pathstra POSIX path
Code
import posixpath
p = '/home/ada/report.pdf'
(posixpath.dirname(p), posixpath.basename(p), posixpath.splitext(p)[1])
Result
('/home/ada', 'report.pdf', '.pdf')

The first two tabs import posixpath directly so they print the same on every computer (on Windows, os.path.join would use '\'). splitext keeps only the last extension — '.gz' for site.tar.gz — and a leading dot is not an extension, so '.bashrc' has none. A trailing slash makes the basename empty: dirname('/var/log/') is '/var/log'. In join, '/etc/passwd' is absolute, so '/srv/app' is discarded; an empty last component adds a trailing separator. The exists tab touches a real (virtual) folder: missing paths give False, never an error.

Members

Functions15
LIVE
os.path.abspath
abspath(path) / os.path.realpath(path, *, strict=False)
Make a path absolute: abspath joins it to the current working directory and normalizes the text; realpath also resolves symlinks on the way.
LIVE
os.path.basename
basename(path) / os.path.dirname(path)
basename is the final component of a path (the file name), dirname is everything before it (the folder). Together they are os.path.split.
LIVE
os.path.commonpath
commonpath(paths) / os.path.commonprefix(list)
commonpath gives the longest shared folder of several paths, component by component. commonprefix compares character by character and can return a path that does not exist.
LIVE
os.path.exists
exists(path) / isfile(path) / isdir(path) / islink(path) / lexists(path)
Ask the file system about a path: does anything exist there, is it a regular file, a folder, a symlink. They return False — never raise — for missing paths.
os.path.expanduser
expanduser(path) / os.path.expandvars(path)
expanduser() replaces a leading ~ or ~user with a home directory; expandvars() replaces $name and ${name} (and %name% on Windows) with environment variable values. Both return a new string and never touch the disk.
LIVE
os.path.getsize
getsize(path) / getmtime(path) / getatime(path) / getctime(path)
One stat field at a time: the size in bytes, and the modification, access and ctime timestamps as float seconds since the epoch. Missing files raise OSError.
LIVE
os.path.isabs
isabs(path)
Is the path absolute? On POSIX: does it start with '/'. On Windows: does it start with a drive and a root (C:\) or two slashes (UNC). Pure string test.
os.path.ismount
ismount(path) / isjunction(path) / isdevdrive(path) / isreserved(path)
Special-path tests: is this a mount point, an NTFS junction, on a Windows Dev Drive, or a name Windows reserves (NUL, CON, trailing dot)? They return False instead of raising for missing paths.
LIVE
os.path.join
join(path, *paths)
Join path components with the right separator, inserting one only where needed. An absolute component discards everything before it.
LIVE
os.path.normpath
normpath(path) / os.path.normcase(path)
Normalize a path as text: collapse doubled separators, drop '.' and resolve 'x/..'. normcase lower-cases and converts / to \ on Windows and does nothing on POSIX.
LIVE
os.path.relpath
relpath(path, start=os.curdir)
The path to `path` as seen from `start` (default: the current directory), using '..' to climb up. Pure computation — nothing is checked on disk.
os.path.samefile
samefile(path1, path2, /) / os.path.sameopenfile(fp1, fp2) / os.path.samestat(stat1, stat2, /)
Do two paths, two open file descriptors or two stat results point at the same file on disk? Decided by device and inode number, not by comparing names or contents.
LIVE
os.path.split
split(path) → (head, tail)
Split a path at its last separator into (head, tail): the folder part and the final component. Trailing separators leave an empty tail.
LIVE
os.path.splitext
splitext(path) → (root, ext)
Split off the file extension: (root, ext) where ext is the last dot and what follows, and root + ext is the original path. Leading dots (dotfiles) are not extensions.
LIVE
os.path.splitroot
splitroot(path) → (drive, root, tail) / os.path.splitdrive(path) → (drive, rest)
Take a path apart at the front: splitroot gives (drive, root, tail), splitdrive gives (drive, rest). On POSIX the drive is always empty; on Windows it is C: or a UNC share.

Common patterns

Build a path next to this script
Resolve data files relative to the source file, not the current directory.
import os
HERE = os.path.dirname(os.path.abspath(__file__))
config_path = os.path.join(HERE, 'config.toml')
Change a file extension
splitext gives (root, ext); glue a new extension to the root.
import os
root, ext = os.path.splitext(filename)
out = root + '.json'
Process files only
listdir returns names; join them with the folder before asking isfile.
import os
files = [n for n in os.listdir(folder) if os.path.isfile(os.path.join(folder, n))]
Show Windows paths from any OS
ntpath and posixpath can be imported everywhere; they are pure string logic.
import ntpath
ntpath.join('C:\\Users', 'ada', 'notes.txt')

Examples

1. Join POSIX components
import posixpath posixpath.join('/home/ada', 'docs', 'report.pdf')
Returns
'/home/ada/docs/report.pdf'
2. Folder, name and extension
import posixpath p = '/home/ada/report.pdf' (posixpath.dirname(p), posixpath.basename(p), posixpath.splitext(p)[1])
Returns
('/home/ada', 'report.pdf', '.pdf')
3. Only the last extension
import posixpath posixpath.splitext('archive.tar.gz')
Returns
('archive.tar', '.gz')
4. Normalize . and ..
import posixpath posixpath.normpath('a//b/./c/../d')
Returns
'a/b/d'
5. os.path is one of the two
import os, posixpath, ntpath os.path is (ntpath if os.name == 'nt' else posixpath)
Returns
True
6. Asking the file system
import os open('notes.txt', 'w').close() (os.path.exists('notes.txt'), os.path.isfile('notes.txt'), os.path.isdir('notes.txt'))
Returns
(True, True, False)
7. Windows rules, on any OS
import ntpath ntpath.join('C:\\Users\\ada', 'notes.txt')
Returns
'C:\\Users\\ada\\notes.txt'

Pitfalls

1. An absolute second argument wins
join discards everything before an absolute component — dangerous with user-supplied names.
join with /etc/passwd
import posixpath
posixpath.join('/srv/uploads', '/etc/passwd')
'/etc/passwd'
check the result stays inside
import posixpath
base = '/srv/uploads'
p = posixpath.normpath(posixpath.join(base, '/etc/passwd'))
posixpath.commonpath([base, p]) == base
False
2. Building paths with + and a hard-coded slash
String concatenation doubles or forgets separators; join inserts exactly one, and only when needed.
+ '/' +
folder = 'logs/'
folder + '/' + 'app.log'
'logs//app.log'
join
import posixpath
posixpath.join('logs/', 'app.log')
'logs/app.log'

When to use

Use it
  • Code that already passes str paths around (os.listdir, os.walk results, argv)
  • Quick one-off string operations: extension, folder, file name
Reach for something else
  • New code that does a lot with paths → pathlib.Path has all of this as methods and properties
  • URLs → urllib.parse (and posixpath only for the path part)
  • Checking before acting (exists then open) → just open and catch FileNotFoundError

Notes

CPython impl
Lib/posixpath.py and Lib/ntpath.py, both built on Lib/genericpath.py (exists, isfile, isdir, getsize, commonprefix, samefile …). os.py picks one at import: sys.modules["os.path"] = posixpath or ntpath
Speed
normpath, splitroot (and on Windows a few more) have C implementations in Modules/posixmodule.c
Bytes
Every function also accepts bytes and then returns bytes; mixing str and bytes raises TypeError

FAQ

Yes — it is ntpath there and posixpath on Linux and macOS. ntpath accepts both \ and /, knows drive letters and UNC shares, and joins with \. To get the same result everywhere, import posixpath or ntpath explicitly.