os.path.relpath

relpath makes both arguments absolute (with abspath, so relative ones depend on the current directory), drops their common leading folders and climbs out of the rest of start with '..'. It never looks at the file system, so it cannot know whether start is a file or a folder — start is always treated as a folder.

os.path functionPython 3.0+Live demo
Common call
os.path.relpath(path, base)
Returns
'static/css/site.css'
Replaces
path[len(base) + 1:] — which breaks on '/srv/app2' vs '/srv/app'
Watch out
On Windows, paths on different drives raise ValueError
os.path.relpath(pathpath — The target. Must not be empty (ValueError: no path specified).type: str | bytes | PathLike · required, startstart — The folder to be relative to. A file name here is treated as a folder too.type: str | bytes | PathLike · default: os.curdir=os.curdir)
→ str | bytes

Demo

Live evaluation
Relative arguments are resolved against a fixed stand-in for os.getcwd(), so the output is the same everywhere.
Try:
Inputs
pathstrtarget path
startstrstart folder
Code
import posixpath
cwd = '/home/ada/project'          # stands in for os.getcwd()
posixpath.relpath(posixpath.join(cwd, '/srv/app/static/css/site.css'), posixpath.join(cwd, '/srv/app'))
Result
'static/css/site.css'

From /srv/app/static, /srv/data is two levels up and one down: '../../data'. A trailing slash on start makes no difference, and identical locations give '.'. With relative inputs both are first placed under the same current directory, so the result does not depend on what that directory is — unless '..' climbs above it.

Parameters

NameTypeRequiredDescription
pathstr | bytes | PathLikeyesThe target. Must not be empty (ValueError: no path specified).
startstr | bytes | PathLikeno (os.curdir)The folder to be relative to. A file name here is treated as a folder too.

Return value

str | bytes — A relative path from start to path; '.' when they are the same.

Common patterns

Show paths relative to the project root
Shorter log lines and reports.
import os
for root, dirs, files in os.walk(PROJECT):
    for name in files:
        print(os.path.relpath(os.path.join(root, name), PROJECT))
Archive member names
Store paths relative to the folder being archived, with forward slashes.
import os
arcname = os.path.relpath(full_path, src_dir).replace(os.sep, '/')
Guard against different drives (Windows)
relpath raises ValueError when no relative path exists.
import os
try:
    shown = os.path.relpath(path, base)
except ValueError:
    shown = path

Examples

1. A file below a folder
import posixpath posixpath.relpath('/srv/app/static/css/site.css', '/srv/app')
Returns
'static/css/site.css'
2. Sideways: '..' needed
import posixpath posixpath.relpath('/srv/data', '/srv/app/static')
Returns
'../../data'
3. Same place
import posixpath posixpath.relpath('/srv/app', '/srv/app')
Returns
'.'
4. Relative to the current folder
import os os.makedirs('a/b') os.path.relpath('a/b', 'a')
Returns
'b'
5. Empty path
import posixpath posixpath.relpath('')
Returns
ValueError: no path specified
6. Windows: different drives
import ntpath ntpath.relpath(r'D:\data', r'C:\Users')
Returns
ValueError: path is on mount 'D:', start on mount 'C:'

Pitfalls

1. Slicing off a prefix by length
A string prefix is not a path prefix: '/srv/app2' starts with '/srv/app'.
path[len(base) + 1:]
base = '/srv/app'
path = '/srv/app2/x.txt'
path[len(base) + 1:]
'/x.txt'
relpath
import posixpath
posixpath.relpath('/srv/app2/x.txt', '/srv/app')
'../app2/x.txt'
2. Passing a file as start
start is always treated as a folder; pass the folder of the file.
start = a file
import posixpath
posixpath.relpath('/site/img/a.png', '/site/pages/about.html')
'../../img/a.png'
start = its folder
import posixpath
posixpath.relpath('/site/img/a.png', posixpath.dirname('/site/pages/about.html'))
'../img/a.png'

When to use

Use it
  • Displaying paths relative to a base folder
  • Building relative links between files (HTML, Markdown, symlinks)
Reach for something else
  • pathlib code → PurePath.relative_to(other, walk_up=True) (3.12+)
  • Checking whether a path is inside a folder → commonpath

Notes

CPython impl
posixpath.relpath: abspath both, split into components, count the shared leading components with commonprefix, then '..' for each remaining start component plus the rest of path
Windows
ntpath.relpath raises ValueError when the paths have different drives (no relative path exists)

FAQ

os.path.relpath(path, base). With pathlib: Path(path).relative_to(base) — which raises ValueError unless path is inside base, or pass walk_up=True (3.12+) to allow '..'.