Path.symlink_to

The path you call it on is the LINK; the argument is what it points to — the reverse of os.symlink(src, dst). Creating symlinks on Windows needs Developer Mode or admin rights, so this page shows hard links in its runnable examples.

pathlib methodPython 3.4+ (readlink 3.9+, hardlink_to 3.10+)
Common call
Path('current').symlink_to('releases/v2')
Returns
None (readlink: the target Path)
Replaces
os.symlink(target, link) / os.link / os.readlink
Watch out
argument order is link.symlink_to(target)
Path.symlink_to(targettarget — What the link points to. For symlinks it is stored as given — a relative target is relative to the link's folder, not the cwd.type: str | os.PathLike · required, target_is_directorytarget_is_directory — symlink_to only: must be True on Windows when target is a folder; ignored elsewhere.type: bool · default: False=False) / .hardlink_to(target) / .readlink()
→ None | Path

Parameters

NameTypeRequiredDescription
targetstr | os.PathLikeyesWhat the link points to. For symlinks it is stored as given — a relative target is relative to the link's folder, not the cwd.
target_is_directoryboolno (False)symlink_to only: must be True on Windows when target is a folder; ignored elsewhere.

Return value

None | Path — symlink_to / hardlink_to return None; readlink returns the stored target as a Path.

Common patterns

"current" release pointer
Point a stable name at a versioned folder.
from pathlib import Path
link = Path('current')
link.unlink(missing_ok=True)
link.symlink_to('releases/v2', target_is_directory=True)
Where does this link point?
readlink gives the stored target; resolve() gives the final absolute path.
from pathlib import Path
link = Path('/usr/bin/python3')
if link.is_symlink():
    print(link.readlink(), link.resolve())
Deduplicate with hard links
Both names share the same data on disk.
from pathlib import Path
Path('copy.iso').hardlink_to('original.iso')

Examples

1. A hard link shares the content
from pathlib import Path Path('orig.txt').write_text('shared', encoding='utf-8') Path('link.txt').hardlink_to('orig.txt') Path('link.txt').read_text(encoding='utf-8')
Returns
'shared'
2. It is the same file
from pathlib import Path Path('orig.txt').touch() Path('link.txt').hardlink_to('orig.txt') Path('link.txt').samefile('orig.txt')
Returns
True
3. Link count goes up
from pathlib import Path Path('orig.txt').touch() Path('link.txt').hardlink_to('orig.txt') Path('orig.txt').stat().st_nlink
Returns
2
4. A hard link is not a symlink
from pathlib import Path Path('orig.txt').touch() Path('link.txt').hardlink_to('orig.txt') Path('link.txt').is_symlink()
Returns
False
5. The link name must be free
from pathlib import Path Path('a').touch() Path('b').touch() try: Path('b').hardlink_to('a') except OSError as e: result = type(e).__name__ result
Returns
'FileExistsError'

Pitfalls

1. Swapping link and target
os.symlink(src, dst) and Path.symlink_to use opposite orders: the Path is the new link. The same holds for hardlink_to — swapped, it looks for a target that does not exist.
target.hardlink_to(link)
from pathlib import Path
Path('data.txt').touch()
try:
    Path('data.txt').hardlink_to('backup.txt')
except OSError as e:
    result = type(e).__name__
result
'FileNotFoundError'
link.hardlink_to(target)
from pathlib import Path
Path('data.txt').touch()
Path('backup.txt').hardlink_to('data.txt')
Path('backup.txt').exists()
True
2. Relative symlink targets
A symlink stores its target text as-is, and the OS interprets a relative target from the link's own folder. Build the target relative to the link, not to the cwd.
relative to cwd
from pathlib import PurePosixPath
link = PurePosixPath('links/app.log')
target = PurePosixPath('logs/app.log')
(link.parent / target).as_posix()
'links/logs/app.log'
relative to the link
from pathlib import PurePosixPath
link = PurePosixPath('links/app.log')
target = PurePosixPath('logs/app.log')
rel = target.relative_to(link.parent, walk_up=True)
(rel.as_posix(), (link.parent / rel).as_posix())
('../logs/app.log', 'links/../logs/app.log')

When to use

Use it
  • Stable names pointing at versioned files or folders (symlink_to)
  • Saving space for identical files on one file system (hardlink_to)
  • Inspecting link targets (readlink)
Reach for something else
  • Copying content → shutil.copy2
  • Hard links across file systems or to folders → not possible; use a symlink

Notes

CPython impl
Lib/pathlib/_local.py: symlink_to = os.symlink(target, self, target_is_directory); hardlink_to = os.link(target, self); readlink = with_segments(os.readlink(self)); each is only defined when os has the function, else UnsupportedOperation
Windows
Creating symlinks needs Developer Mode or the SeCreateSymbolicLinkPrivilege (usually admin); hard links work on NTFS without it
Changed in 3.13
Missing OS support raises UnsupportedOperation instead of NotImplementedError
link_to
The old Path.link_to (with the reversed argument order) was removed in 3.12; use hardlink_to

FAQ

Path('link_name').symlink_to('target'). The Path is the link, the argument is what it points to. On Windows pass target_is_directory=True for folders and enable Developer Mode.