PurePath.relative_to
Both are lexical: they compare path components, never the file system. relative_to strips the other path off the front; with walk_up=True (3.12+) it may climb with ".." like os.path.relpath.
Demo
from pathlib import PurePosixPath PurePosixPath('/srv/app/static/logo.svg').relative_to('/srv/app')
The checks are per component, so '/srv/application' is not under '/srv/app' even though the string starts with it. They are also purely textual: '/srv/app/../secret' counts as inside '/srv/app' because '..' is not resolved — call resolve() first when that matters. With walk_up=True the error changes: an absolute path and a relative one have different anchors, and a '..' inside other cannot be walked.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| other | str | os.PathLike | yes | The base path. |
| walk_up | bool | no (False) | relative_to only (3.12+): allow '..' segments when this path is not under other. Keyword-only in practice. |
Return value
PurePath | bool — relative_to returns a relative path; is_relative_to returns True or False.
Common patterns
from pathlib import Path root = Path.cwd() for p in sorted(root.rglob('*.py')): print(p.relative_to(root).as_posix())
from pathlib import Path base = Path('uploads').resolve() target = (base / name).resolve() if not target.is_relative_to(base): raise PermissionError(name)
from pathlib import Path src, dst = Path('src'), Path('build') for f in src.rglob('*.txt'): out = dst / f.relative_to(src)
Examples
Pitfalls
'/srv/app2/secret'.startswith('/srv/app')
from pathlib import PurePosixPath PurePosixPath('/srv/app2/secret').is_relative_to('/srv/app')
from pathlib import PurePosixPath PurePosixPath('/srv/app/../../etc/passwd').is_relative_to('/srv/app')
from pathlib import Path base = Path('app').resolve() (base / '../../etc/passwd').resolve().is_relative_to(base)
from pathlib import PurePosixPath PurePosixPath('/srv/logs').relative_to('/srv/app')
from pathlib import PurePosixPath PurePosixPath('/srv/logs').relative_to('/srv/app', walk_up=True)
When to use
- Displaying paths relative to a root folder
- Mirroring a directory tree under a new base
- Containment checks (after resolve())
- Paths with unresolved ".." or symlinks → resolve() both sides first
- Paths on different drives (Windows) → no relative path exists; handle the ValueError
Notes
FAQ
target.relative_to(base). If target is not inside base, pass walk_up=True (Python 3.12+) to get a path with '..' segments, like os.path.relpath.