os.path.commonpath
Use commonpath. commonprefix is a plain string function living in os.path for historical reasons: for '/usr/lib' and '/usr/local' it answers '/usr/l'. commonpath splits into components first, ignores '' and '.' components, and refuses to mix absolute and relative paths.
Demo
import posixpath (posixpath.commonpath(['/srv/app/static', '/srv/app/templates']), posixpath.commonprefix(['/srv/app/static', '/srv/app/templates']))
commonprefix stops at the first differing character, so '/srv/app/static' and '/srv/app/templates' give '/srv/app/' and '/usr/lib' with '/usr/local' gives the meaningless '/usr/l'. commonpath works on whole components and ignores './'. Mixing '/srv' and 'srv' raises ValueError: Can't mix absolute and relative paths. In the second tab only avatar.png stays inside: '..', an absolute name and the look-alike folder uploads-old all leave /srv/uploads, and a startswith(base) test would have let uploads-old through.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| paths | iterable of str | bytes | PathLike | yes | commonpath: two or more paths (one is fine). Any iterable since 3.13; before, a sequence. |
| list | list of str | yes | commonprefix: any strings (or lists) — compared character by character. |
Return value
str — commonpath: a real sub-path shared by all (no trailing separator). commonprefix: the longest common string prefix ('' for an empty list).
Common patterns
import os def inside(base, path): base = os.path.realpath(base) path = os.path.realpath(path) return os.path.commonpath([base, path]) == base
import os root = os.path.commonpath(file_list)
import os prefix = os.path.commonprefix(['interstellar', 'internet', 'interval'])
Examples
Pitfalls
import posixpath posixpath.commonprefix(['/home/ada', '/home/adam'])
import posixpath posixpath.commonpath(['/home/ada', '/home/adam'])
'/srv/uploads-old/x'.startswith('/srv/uploads')
import posixpath posixpath.commonpath(['/srv/uploads', '/srv/uploads-old/x']) == '/srv/uploads'
import posixpath base = '/srv/uploads' p = posixpath.join(base, '../etc/passwd') posixpath.commonpath([base, p]) == base
import posixpath base = '/srv/uploads' p = posixpath.normpath(posixpath.join(base, '../etc/passwd')) posixpath.commonpath([base, p]) == base
When to use
- Finding the shared folder of several paths (commonpath)
- Path-traversal checks after normpath / realpath
- Paths → never commonprefix
- pathlib code → PurePath.is_relative_to(base) after resolve()
Notes
FAQ
commonpath compares whole path components and returns a valid path ('/usr' for /usr/lib and /usr/local). commonprefix compares characters and can return a half name ('/usr/l'). The docs warn that commonprefix is not secure for paths.