This rule raises an issue when multiple startswith() or endswith() calls on the same string are chained together using the or operator to check for different prefixes or suffixes.

Why is this an issue?

When you need to check if a string starts or ends with one of several possible values, chaining multiple startswith() or endswith() calls with the or operator creates unnecessary verbosity and redundancy. Each additional prefix or suffix requires a separate method call and another or operator, making the condition increasingly difficult to read and maintain.

This pattern is particularly common when validating file extensions, URL protocols, or any scenario where you need to check for multiple valid prefixes or suffixes.

What is the potential impact?

The consolidated form is easier to read and maintain, reducing the cognitive load on developers reviewing or modifying the code. The performance gain from a single consolidated call is modest but accumulates in loops or hot paths.

How to fix it

Replace multiple startswith() or endswith() calls connected by or operators with a single call that uses a tuple containing all the prefixes or suffixes to check.

Both startswith() and endswith() methods accept a tuple of strings as their first argument. When you pass a tuple, the method checks if the string starts or ends with any of the values in that tuple. This has two main benefits:

Code examples

Noncompliant code example

if s.startswith("http://") or s.startswith("https://"):  # Noncompliant: use a tuple argument instead
    process_url(s)

Compliant solution

if s.startswith(("http://", "https://")):
    process_url(s)

Noncompliant code example

if filename.endswith(".jpg") or filename.endswith(".png") or filename.endswith(".gif"):  # Noncompliant: use a tuple argument instead
    display_image(filename)

Compliant solution

if filename.endswith((".jpg", ".png", ".gif")):
    display_image(filename)

Resources

Documentation

Related rules