Skip to content

C901, PLR0912 and PLR0915 treat match/case as one statement #11421

@jaap3

Description

@jaap3

Using ruff 0.4.4 the following code triggers C901, PLR0912 and PLR0915:

def grades_to_average(grades):
    numbers = []
    for grade in grades:
        if grade in {"F-", "F", "F+", "E-", "E"}:
            numbers.append(0)
        elif grade == "E+":
            numbers.append(.3)
        elif grade == "D-":
            numbers.append(.7)
        elif grade == "D":
            numbers.append(1.0)
        elif grade == "D+":
            numbers.append(1.3)
        elif grade == "C-":
            numbers.append(1.7)
        elif grade == "C":
            numbers.append(2.0)
        elif grade == "C+":
            numbers.append(2.3)
        elif grade == "B-":
            numbers.append(2.7)
        elif grade == "B":
            numbers.append(3.0)
        elif grade == "B+":
            numbers.append(3.3)
        elif grade == "A-":
            numbers.append(3.7)
        elif grade in {"A", "A+"}:
            numbers.append(4.0)
        else:
            raise ValueError(f"Unknown grade: {grade}")

    try:
        avg = sum(numbers) / len(numbers)
    except ZeroDivisionError:
        avg = 0

    if avg < 0.3:
        avg_grade = "F"
    elif avg < .7:
        avg_grade = "E+"
    elif avg < 1.0:
        avg_grade = "D-"
    elif avg < 1.3:
        avg_grade = "D"
    elif avg < 1.7:
        avg_grade = "D+"
    elif avg < 2.0:
        avg_grade = "C-"
    elif avg < 2.3:
        avg_grade = "C"
    elif avg < 2.7:
        avg_grade = "C+"
    elif avg < 3.0:
        avg_grade = "B-"
    elif avg < 3.3:
        avg_grade = "B"
    elif avg < 3.7:
        avg_grade = "B+"
    elif avg < 4.0:
        avg_grade = "A-"
    elif avg >= 4.0:
        avg_grade = "A"
    else:
        raise ValueError(f"Unexpected average: {avg}")
    return avg_grade

The equivalent code that uses match/case however does not:

def grades_to_average(grades):
    numbers = []
    for grade in grades:
        match grade:
            case "F-" | "F" | "F+" | "E-" | "E":
                numbers.append(0)
            case "E+":
                numbers.append(.3)
            case "D-":
                numbers.append(.7)
            case "D":
                numbers.append(1.0)
            case "D+":
                numbers.append(1.3)
            case "C-":
                numbers.append(1.7)
            case "C":
                numbers.append(2.0)
            case "C+":
                numbers.append(2.3)
            case "B-":
                numbers.append(2.7)
            case "B":
                numbers.append(3.0)
            case "B+":
                numbers.append(3.3)
            case "A-":
                numbers.append(3.7)
            case "A" | "A+":
                numbers.append(4.0)
            case _:
                raise ValueError(f"Unknown grade: {grade}")

    try:
        avg = sum(numbers) / len(numbers)
    except ZeroDivisionError:
        avg = 0

    match avg:
        case avg if avg < .3:
            avg_grade = "F"
        case avg if avg < .7:
            avg_grade = "E+"
        case avg if avg < 1.0:
            avg_grade = "D-"
        case avg if avg < 1.3:
            avg_grade = "D"
        case avg if avg < 1.7:
            avg_grade = "D+"
        case avg if avg < 2.0:
            avg_grade = "C-"
        case avg if avg < 2.3:
            avg_grade = "C"
        case avg if avg < 2.7:
            avg_grade = "C+"
        case avg if avg < 3.0:
            avg_grade = "B-"
        case avg if avg < 3.3:
            avg_grade = "B"
        case avg if avg < 3.7:
            avg_grade = "B+"
        case avg if avg < 4.0:
            avg_grade = "A-"
        case avg if avg >= 4.0:
            avg_grade = "A"
        case _:
            raise ValueError(f"Unexpected average: {avg}")
    return avg_grade

Is this intentional? Should each case count as a conditional?

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedContributions especially welcomeruleImplementing or modifying a lint rule

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions