Building paths with os.path

os · Essentials · 10 min

Gluing paths together with string concatenation breaks across platforms and on edge cases os.path already handles.

os.path is a submodule of string functions for paths. It works with plain str (or bytes) values and uses the right rules for the current platform.

  • os.path.join("data", "2024", "a.csv") inserts the separator. If a later part is absolute, earlier parts are discarded: join("data", "/etc") is "/etc".
  • os.path.split(p) returns (head, tail); dirname and basename give each half.
  • os.path.splitext("a.tar.gz") gives ("a.tar", ".gz"): only the last extension.
  • os.path.abspath(p) makes a path absolute against the cwd; normpath collapses a/./b/../c to a/c; realpath also resolves symbolic links.
  • os.path.expanduser("~/x") expands the home directory.

Queries touch the file system:

  • exists(p), isfile(p), isdir(p), islink(p)
  • getsize(p) and getmtime(p) (size in bytes, modification time as a timestamp)

exists returns False for broken symlinks and for paths you lack permission to inspect, and the answer can change right after you ask. For files you are about to open, it is often cleaner to just try and catch FileNotFoundError.

In new code, pathlib.Path offers the same operations as methods on path objects. os.path remains everywhere in existing code, and every os function accepts both strings and Path objects.

Example

import os.path as p

path = p.join("data", "2024", "sales.tar.gz")
print(path)                       # data/2024/sales.tar.gz
print(p.split(path))              # ('data/2024', 'sales.tar.gz')
print(p.splitext(path)[1])        # .gz
print(p.join("data", "/etc"))     # /etc
print(p.normpath("a/./b/../c"))   # a/c
print(p.isabs(p.abspath("x")))    # True
print(p.exists("no/such/file"))   # False
print(p.expanduser("~") != "~")   # True

Remember

  • os.path.join adds separators and restarts at any absolute component
  • splitext splits off only the last extension
  • exists/isfile/isdir hit the file system; the string functions do not

Watch out for

  • Writing base + '/' + name; use os.path.join so separators and edge cases are handled.
  • Passing user input as a later join argument; an absolute value silently replaces your base directory.

Check yourself

  1. What does os.path.join('/srv/uploads', '/etc/passwd') return on Linux?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/os.path.html