https://github.com/python/cpython/commit/c99097ee97edff0cd2ccd254690f14015f4368ce
commit: c99097ee97edff0cd2ccd254690f14015f4368ce
branch: main
author: Nishant Bhandari <[email protected]>
committer: JelleZijlstra <[email protected]>
date: 2026-10-20T17:04:58-07:00
summary:

gh-159868: Accept UserString arguments in startswith and endswith (#159869)

files:
A Misc/NEWS.d/next/Library/2026-10-20-16-00-00.gh-issue-159868.qLhYkP.rst
M Lib/collections/__init__.py
M Lib/test/test_userstring.py

diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py
index 5dbcac19e7a9272..f23129d7e3275e7 100644
--- a/Lib/collections/__init__.py
+++ b/Lib/collections/__init__.py
@@ -1536,6 +1536,8 @@ def encode(self, encoding='utf-8', errors='strict'):
         return self.data.encode(encoding, errors)
 
     def endswith(self, suffix, start=0, end=_sys.maxsize):
+        if isinstance(suffix, UserString):
+            suffix = suffix.data
         return self.data.endswith(suffix, start, end)
 
     def expandtabs(self, tabsize=8):
@@ -1646,6 +1648,8 @@ def splitlines(self, keepends=False):
         return self.data.splitlines(keepends)
 
     def startswith(self, prefix, start=0, end=_sys.maxsize):
+        if isinstance(prefix, UserString):
+            prefix = prefix.data
         return self.data.startswith(prefix, start, end)
 
     def strip(self, chars=None):
diff --git a/Lib/test/test_userstring.py b/Lib/test/test_userstring.py
index cc85c06bf933633..1ba5b661aba016b 100644
--- a/Lib/test/test_userstring.py
+++ b/Lib/test/test_userstring.py
@@ -121,6 +121,32 @@ def test_mixed_cmp(self):
         self._assert_cmp(a, 'a', 1)
         self._assert_cmp(a, 'ac', -1)
 
+    @support.subTests('str_class', (UserString, UserStringSubclass))
+    def test_startswith_userstring(self, str_class):
+        s = UserString('hello world')
+        self.assertTrue(s.startswith(str_class('hello')))
+        self.assertFalse(s.startswith(str_class('world')))
+        self.assertTrue(s.startswith(str_class('')))
+        self.assertFalse(s.startswith(str_class('hello world!')))
+        self.assertTrue(s.startswith(str_class('world'), 6))
+        self.assertTrue(s.startswith(str_class('world'), 6, 11))
+        self.assertFalse(s.startswith(str_class('world'), 6, 10))
+        self.assertTrue(s.startswith(str_class('world'), -5))
+        self.assertTrue(s.startswith(str_class('world'), start=6, end=11))
+
+    @support.subTests('str_class', (UserString, UserStringSubclass))
+    def test_endswith_userstring(self, str_class):
+        s = UserString('hello world')
+        self.assertTrue(s.endswith(str_class('world')))
+        self.assertFalse(s.endswith(str_class('hello')))
+        self.assertTrue(s.endswith(str_class('')))
+        self.assertFalse(s.endswith(str_class('hello world!')))
+        self.assertTrue(s.endswith(str_class('world'), 6))
+        self.assertTrue(s.endswith(str_class('hello'), 0, 5))
+        self.assertFalse(s.endswith(str_class('hello'), 1, 5))
+        self.assertTrue(s.endswith(str_class('hello'), -11, -6))
+        self.assertTrue(s.endswith(str_class('hello'), start=0, end=5))
+
     def test_rmod(self):
         class ustr2(UserString):
             pass
diff --git 
a/Misc/NEWS.d/next/Library/2026-10-20-16-00-00.gh-issue-159868.qLhYkP.rst 
b/Misc/NEWS.d/next/Library/2026-10-20-16-00-00.gh-issue-159868.qLhYkP.rst
new file mode 100644
index 000000000000000..fa664aa31d42dad
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-20-16-00-00.gh-issue-159868.qLhYkP.rst
@@ -0,0 +1,3 @@
+Allow :meth:`!collections.UserString.startswith` and
+:meth:`!collections.UserString.endswith` to accept a
+:class:`~collections.UserString` instance as the prefix or suffix argument.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to