str.removesuffix()

Strip an exact suffix — the intent-preserving alternative to rstrip that does not surprise you with character-set semantics.

String methodPython 3.9+Live demo
Common call
name.removesuffix(".py")
Returns
new str — the original is unchanged
Replaces
the classic `s[:-len(suffix)] if s.endswith(suffix) else s` idiom
Watch out
exact substring match — not a set of characters like rstrip
str.removesuffix(suffixsuffixThe exact substring to remove from the end. If the string does not end with this exact substring, the original is returned unchanged.type: str · required)
str

Demo

Live evaluation
Try:
Inputs
stringstrthe source
suffixstrexact suffix
Output
'basename.py'.removesuffix('.py')
'basename'

removesuffix strips an EXACT substring from the end — at most once. If the string does not end with the suffix, the original is returned unchanged. Unlike rstrip, the argument is a substring, not a character set — ".py" removes exactly ".py" once, not any trailing ".", "p", or "y" characters in any order.

Parameters

NameTypeRequiredDescription
suffixstryesThe exact substring to remove from the end. If the string does not end with this exact substring, the original is returned unchanged.

Return value

strA copy of the string with the exact SUBSTRING `suffix` removed from the end, IF the string ends with it. Otherwise the string is returned unchanged. Removes at most one occurrence.

Common patterns

Strip a file extension
The canonical use case — clean and unambiguous.
stem = filename.removesuffix(".py")
Drop a version suffix
Trim exact known suffixes without slicing math.
name = package.removesuffix("-1.0.0")
Symmetric prefix + suffix strip
Chain removeprefix and removesuffix for wrapped strings.
inner = s.removeprefix("(").removesuffix(")")

Examples

1. File extension
"basename.py".removesuffix(".py")
Returns
"basename"
2. Log extension
"access.log".removesuffix(".log")
Returns
"access"
3. Not a suffix
"hello".removesuffix("xy")
Returns
"hello" # unchanged
4. String == suffix
"hello".removesuffix("hello")
Returns
""
5. Removes once only
"test.py.py".removesuffix(".py")
Returns
"test.py" # not "test"
6. Empty suffix is no-op
"hello".removesuffix("")
Returns
"hello"

Pitfalls

1. Removes AT MOST ONE occurrence
removesuffix strips the suffix once and stops. Doubled suffixes (or triple, etc.) leave the rest in place. Loop or use a while for multi-removal.
One shot
"test.py.py".removesuffix(".py")
"test.py" # still has ".py"
Loop for all
s = "test.py.py"
while s.endswith(".py"):
    s = s.removesuffix(".py")
"test"
2. Empty suffix returns the original unchanged
Passing "" is a no-op — every string trivially ends with the empty string, but removing 0 characters leaves the original alone.
Empty is no-op
"hello".removesuffix("")
"hello"
Guard for empty
if suffix:
    s = s.removesuffix(suffix)
explicit intent
3. Different from rstrip — exact match, not character set
This is the whole point of the method. rstrip(".py") strips any trailing ".", "p", or "y" in any order and any repetition. removesuffix(".py") only matches the literal suffix ".py".
rstrip over-strips
"happy.py".rstrip(".py")
"ha" # ate every trailing "y", "p", "."
removesuffix is precise
"happy.py".removesuffix(".py")
"happy" # just the last three chars
4. Not available before Python 3.9
Added in 3.9. On older Pythons use the classic idiom.
AttributeError on 3.8
"basename.py".removesuffix(".py")   # 3.8
AttributeError: 'str' object has no attribute 'removesuffix'
Portable idiom
def removesuffix(s, x):
    return s[:-len(x)] if x and s.endswith(x) else s
works everywhere

When to use

Use it
  • Stripping a KNOWN, exact trailing substring
  • File extension removal
  • Version or namespace suffix trimming
  • Any place rstrip would have been the wrong tool because of character-set semantics
Reach for something else
  • Multi-occurrence removal → loop with while + endswith
  • Character-set removal → str.rstrip is the correct tool
  • Case-insensitive suffix removal → casefold both sides, or use regex
  • Python 3.8 or earlier → use the s[:-len(x)] idiom instead

Notes

Complexity
O(n) — one linear scan to test the suffix, then one slice
Return
A new string; the original is unchanged (strings are immutable)
CPython impl
Objects/unicodeobject.c :: unicode_removesuffix — introduced with PEP 616
Memory
Allocates one new string when the suffix matched; returns the original when not
Thread-safe
Yes — strings are immutable

FAQ

Because rstrip strips a CHARACTER SET, not a substring. "happy.py".rstrip(".py") strips trailing y, p, and dot in any combination — you end up with "ha", not "happy". removesuffix does exact-suffix removal, which is almost always what people meant when they reached for rstrip.

History

3.9
removesuffix() introduced along with removeprefix() via PEP 616 — replacing hand-rolled slice idioms.