Batu Lab NotesPractical developer guides

Find a relative path below a known root

By Batu ยท English technical notes

Also published in our Blogger archive.

Find a relative path below a known root

When a program already knows a root directory, Path.relative_to can express a descendant without repeating that root. The temporary fixture creates project/src/api.py. Calling file_path.relative_to(root) removes the project portion and returns the relative path src/api.py. This is useful when a manifest, log entry, or comparison should be scoped to a known project rather than expose an absolute location.

The assertions check both directions relevant to the controlled fixture: the derived path has the expected components, and joining it to this root recreates the original path. as_posix() is used only for output, giving forward slashes on every platform and avoiding the randomly generated temporary-directory name.

pathlib was introduced in Python 3.4, and this exact recipe requires Python 3.5+ because it calls Path.write_text. With the default walk_up=False, relative_to raises ValueError when the candidate does not begin with the supplied root. The operation is lexical: it does not inspect the filesystem. Symlinks or unresolved .. components can therefore make lexical ancestry differ from resolved ancestry; resolve paths first if that distinction matters. The example creates no symlinks, so it does not establish symlink policy. The official relative_to documentation describes both the failure behavior and lexical limitation. Path.write_text documents its Python 3.5 introduction, and TemporaryDirectory provides the disposable fixture root.

AI-assistance disclosure: AI helped draft this educational article.

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    root = Path(directory) / "project"
    file_path = root / "src" / "api.py"
    file_path.parent.mkdir(parents=True)
    file_path.write_text("pass\n", encoding="utf-8")

    relative = file_path.relative_to(root)

    assert relative == Path("src/api.py")
    assert root / relative == file_path

    print(relative.as_posix())
src/api.py