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.