PurePath.with_suffix

The three with_* methods rewrite the final component: with_suffix swaps (or adds, or removes) the extension, with_stem keeps the extension, with_name replaces everything after the last slash. They are pure — nothing is renamed on disk.

pathlib methodPython 3.4+ (with_stem 3.9+)Live demo
Common call
p.with_suffix('.webp')
Returns
a new path — pass it to rename() to actually move a file
Replaces
os.path.splitext(p)[0] + ".webp"
Watch out
the suffix needs its dot: with_suffix('gz') is a ValueError
PurePath.with_suffix(suffix) / .with_stem(stem) / .with_name(name)
→ PurePath

Demo

Live evaluation
Replace the last suffix, add one, or remove it with an empty string.
Try:
Inputs
pathstra path
suffixstrnew suffix
Code
from pathlib import PurePosixPath
PurePosixPath('photos/cat.jpeg').with_suffix('.webp')
Result
PurePosixPath('photos/cat.webp')

with_suffix works on the stem, so on site.tar.gz only .gz is replaced. A suffix without a leading dot is rejected, and so is any path with an empty name like '/'. with_stem('') on a file that has a suffix fails because the result would be just the extension. with_name refuses a name containing a slash — use / to add a sub-path instead.

Parameters

NameTypeRequiredDescription
suffixstryeswith_suffix: '' removes the last suffix; otherwise it must start with '.' and be longer than one character.
stemstryeswith_stem: the new stem; the current suffix is appended.
namestryeswith_name: the complete new last component; must not be empty, '.', or contain a separator.

Return value

PurePath — A new path of the same class; only the last component differs.

Common patterns

Actually rename the file on disk
with_suffix only builds the new path; rename() moves the file.
from pathlib import Path
p = Path('draft.txt')
p.rename(p.with_suffix('.md'))
Write a converted copy next to the original
Keep the name, change the extension.
from pathlib import Path
for src in Path('images').glob('*.png'):
    convert(src, src.with_suffix('.webp'))
Replace a double extension
Strip the old suffixes first, then add the new one.
from pathlib import PurePosixPath
p = PurePosixPath('backup.tar.gz')
p.with_suffix('').with_suffix('.zip')  # backup.zip

Examples

1. Change the extension
from pathlib import PurePosixPath PurePosixPath('data/report.csv').with_suffix('.xlsx')
Returns
PurePosixPath('data/report.xlsx')
2. Remove the extension
from pathlib import PurePosixPath PurePosixPath('data/report.csv').with_suffix('')
Returns
PurePosixPath('data/report')
3. Keep the extension, change the stem
from pathlib import PurePosixPath PurePosixPath('img/cat.png').with_stem('cat@2x')
Returns
PurePosixPath('img/cat@2x.png')
4. Replace the whole name
from pathlib import PurePosixPath PurePosixPath('/etc/hosts').with_name('hostname')
Returns
PurePosixPath('/etc/hostname')
5. Double extension: both steps
from pathlib import PurePosixPath PurePosixPath('backup.tar.gz').with_suffix('').with_suffix('.zip')
Returns
PurePosixPath('backup.zip')
6. The original is untouched
from pathlib import PurePosixPath p = PurePosixPath('a.txt') p.with_suffix('.md') p
Returns
PurePosixPath('a.txt')
7. A dotfile gains a suffix
from pathlib import PurePosixPath PurePosixPath('.env').with_suffix('.bak')
Returns
PurePosixPath('.env.bak')

Pitfalls

1. Forgetting the dot
The suffix must include its leading dot.
'md'
from pathlib import PurePosixPath
PurePosixPath('notes.txt').with_suffix('md')
ValueError: Invalid suffix 'md'
'.md'
from pathlib import PurePosixPath
PurePosixPath('notes.txt').with_suffix('.md')
PurePosixPath('notes.md')
2. Calling with_* on a path without a name
'/', '.' and '' have an empty name, so there is nothing to replace.
Path('.')
from pathlib import PurePosixPath
PurePosixPath('.').with_name('out')
ValueError: PurePosixPath('.') has an empty name
join instead
from pathlib import PurePosixPath
PurePosixPath('.') / 'out'
PurePosixPath('out')
3. Expecting the file to be renamed
with_suffix returns a new path object. The file on disk keeps its name until you call rename() or replace().
with_suffix only
from pathlib import Path
p = Path('draft.txt')
p.touch()
p.with_suffix('.md')
sorted(x.name for x in Path('.').iterdir())
['draft.txt']
rename(with_suffix)
from pathlib import Path
p = Path('draft.txt')
p.touch()
p.rename(p.with_suffix('.md'))
sorted(x.name for x in Path('.').iterdir())
['draft.md']

When to use

Use it
  • Deriving output file names from input names
  • Changing, adding or removing an extension
Reach for something else
  • Renaming on disk → combine with rename() / replace()
  • Adding a sub-path → the / operator

Notes

CPython impl
with_name is defined on PurePath (Lib/pathlib/_local.py) and raises "Invalid name" / "has an empty name"; with_stem and with_suffix come from PurePathBase in _abc.py and call with_name
ValueError texts
Invalid suffix 'md' · Invalid name 'a/b' · PurePosixPath('/') has an empty name · … has a non-empty suffix (with_stem(''))
Changed in 3.14
with_suffix('.') becomes valid; in 3.13 it raises ValueError: Invalid suffix '.'

FAQ

p.with_suffix('.new') gives the new path; p.rename(p.with_suffix('.new')) renames the file. Include the dot.