startswith() and endswith() methods returning the matched value

This is a proposal to change the behaviour of the startswith() and endswith() methods for str, bytes and bytearray objects, making them return the matched value instead of the True boolean. Here a str example with the endswith() method: domain = "python.org" if domain.endswith(".fr"): print(".fr") elif domain.endswith(".com"): print(".com") elif domain.endswith("org"): print(".org") With the ".org" non-empty str returned by the endswith() method instead of the True boolean, the above code will work in the same manner; moreover as startswith() and endswith() methods support a tuple as argument, and as the PEP 572 walrus operator is supported since Python 3.8, we can now write this code in a more compact and pythonic way: if suffix := domain.endswith((".fr", ".com", ".org"): print(suffix) As the PEP 616 new methods removeprefix() and removesuffix() don't support a tuple as argument, it will provide a nice and pythonic way to do it: if suffix := domain.endswith((".fr", ".com", ".org"): print(domain[:-len(suffix)]) Hope I don't miss something obvious and that this proposal could interest some people. Best regards Florent

On Mon, Aug 9, 2021 at 1:42 PM <fgallaire@gmail.com> wrote:
Unfortunately this would break backward compatibility, since it's currently guaranteed that they return precisely True or False, and that can be used (eg) for indexing. To maintain that, you'd have to create new methods which return the matched value or None (and can then define startswith/endswith as the boolification of that). For instance: domain.findsuffix((".fr", ".com", ".org")) ChrisA

On Mon, Aug 9, 2021 at 10:32 PM Samuel Freilich <sfreilich@google.com> wrote:
that can be used (eg) for indexing
Even without it being used in as complicated a way as that it's still not backward compatible because of the trivial case, as foo.endswith("") is True.
I was talking specifically about the original, which can be depended upon to return True or False. Changing the return value would break anything that depends on that. Not sure what you're referring to. ChrisA

On Tue, Aug 10, 2021 at 12:03 AM Simão Afonso <simao.afonso@powertools-tech.com> wrote:
Yep, that's also a problem, but I don't understand why my comment about "can be used eg for indexing" was being quoted for context there. I was talking about how you could do something like this: protocol = ("ws:", "wss:")[url.startswith("https:")] If the return value changes, this breaks. ChrisA

I was just saying that using the bool return value as an index is a bit obscure, compared to using it as a condition in an if statement. But even in the more common use, returning the matched string is still a chance in behavior. On Mon, Aug 9, 2021, 10:13 AM Chris Angelico <rosuav@gmail.com> wrote:

Two possibilities: 1) the perfectly backward compatible, retrun False 2) the more pythonic, return the empty value of the object (i.e. "" for str)

I missed the point of your question, my answer was about False replacement as I hadn't precised it previously. So now that I understand your question, yes you're right there's an issue here. This pleads in the direction of two new methods: prefix() and suffix(). As PEP 616 just added the two new methods removeprefix() and removesuffix(), it seems possible to do it. I personally think that prefix() and suffix() have more usecases than this ones. Florent Le lun. 9 août 2021 à 07:02, Serhiy Storchaka <storchaka@gmail.com> a écrit :
-- FLOSS Engineer & Lawyer

On Mon, Aug 9, 2021 at 1:42 PM <fgallaire@gmail.com> wrote:
Unfortunately this would break backward compatibility, since it's currently guaranteed that they return precisely True or False, and that can be used (eg) for indexing. To maintain that, you'd have to create new methods which return the matched value or None (and can then define startswith/endswith as the boolification of that). For instance: domain.findsuffix((".fr", ".com", ".org")) ChrisA

On Mon, Aug 9, 2021 at 10:32 PM Samuel Freilich <sfreilich@google.com> wrote:
that can be used (eg) for indexing
Even without it being used in as complicated a way as that it's still not backward compatible because of the trivial case, as foo.endswith("") is True.
I was talking specifically about the original, which can be depended upon to return True or False. Changing the return value would break anything that depends on that. Not sure what you're referring to. ChrisA

On Tue, Aug 10, 2021 at 12:03 AM Simão Afonso <simao.afonso@powertools-tech.com> wrote:
Yep, that's also a problem, but I don't understand why my comment about "can be used eg for indexing" was being quoted for context there. I was talking about how you could do something like this: protocol = ("ws:", "wss:")[url.startswith("https:")] If the return value changes, this breaks. ChrisA

I was just saying that using the bool return value as an index is a bit obscure, compared to using it as a condition in an if statement. But even in the more common use, returning the matched string is still a chance in behavior. On Mon, Aug 9, 2021, 10:13 AM Chris Angelico <rosuav@gmail.com> wrote:

Two possibilities: 1) the perfectly backward compatible, retrun False 2) the more pythonic, return the empty value of the object (i.e. "" for str)

I missed the point of your question, my answer was about False replacement as I hadn't precised it previously. So now that I understand your question, yes you're right there's an issue here. This pleads in the direction of two new methods: prefix() and suffix(). As PEP 616 just added the two new methods removeprefix() and removesuffix(), it seems possible to do it. I personally think that prefix() and suffix() have more usecases than this ones. Florent Le lun. 9 août 2021 à 07:02, Serhiy Storchaka <storchaka@gmail.com> a écrit :
-- FLOSS Engineer & Lawyer
participants (6)
-
Chris Angelico
-
fgallaire@gmail.com
-
Florent Gallaire
-
Samuel Freilich
-
Serhiy Storchaka
-
Simão Afonso