Skip to content

Allow # noqa: SIM103 on last return #16250

Description

@JKE-be

Description

searched: SIM103 Ignore

snippet:

def func(a, b):
    if a == 0:
        return False

    a = a + b / a
    if a != 1:
        return False

    a = a * b
    if a > 1:
        return False

    return True  # noqa SIM103

Command: ruff check /tmp/ruff.py

Output:

/tmp/ruff.py:10:5: SIM103 Return the negated condition directly
   |
 9 |       a = a * b
10 | /     if a > 1:
11 | |         return False
12 | |
13 | |     return True  # noqa SIM103
   | |_______________^ SIM103
   |
   = help: Inline condition

version: ruff 0.9.6

config:

fix = false
show-fixes = true
line-length = 120
output-format = "full"
target-version = "py38"

[lint]
ignore = [
    "E501",
    "E731",
    "RUF012",   # mutable-class-default
]
select = [
    "B",   # flake8-bugbear
    "E",   # pycodestyle
    "F",   # Pyflakes
    "G",   # flake8-logging-format
    "ISC", # flake8-implicit-str-concat
    "PERF",# perflint
    "RUF", # ruff specific rules
    "SIM", # flake8-simplify
    "W",   # pycodestyle
]

Expected (Nice to have feature)

Allow ignoring SIM103 without requiring the # noqa comment on the last if statement itself. The goal is to enable other developers to add another if after the last one without modifying the previous condition.

This is why I don’t want to refactor the last condition as suggested by Ruff. It’s a special case, but I don’t want to ignore SIM103 for the entire file or all files, as I still find it useful in 99% of cases.

Currently, I need to add # noqa on the last ìf to get All checks passed!

def func(a, b):
    if a == 0:
        return False

    a = a + b / a
    if a != 1:
        return False

    a = a * b
-   if a > 1:  # noqa SIM103 
+   if a > 1:
        return False

-   return True
+   return True  # noqa SIM103 

Activity

  1. changed the title [-]allow noqa SIM103 on last return[/-] [+]Allow `# noqa SIM103` on last return[/+] on Feb 19, 2025
  2. InSyncWithFoo commented on Feb 19, 2025

    @InSyncWithFoo
    Contributor

    Irrelevant, but the comment should have a colon: # noqa: SIM103. See also PGH004.

  3. changed the title [-]Allow `# noqa SIM103` on last return[/-] [+]Allow `# noqa: SIM103` on last return[/+] on Feb 19, 2025
  4. dylwil3 commented on Feb 19, 2025

    @dylwil3
    Collaborator

    This is an interesting case, thank you!

    As far as I can tell this would require some fairly substantial changes to how error suppression currently works for multi-line diagnostic ranges. Note that, in your example, that final return statement is part of the diagnostic range, it's just that in general the in-line suppression comment is supposed to be on the first line of a diagnostic.

    So I think supporting this may require some design discussion to understand whether it should be done and what the ramifications might be.

  5. added
    suppressionRelated to supression of violations e.g. noqa
    needs-decisionAwaiting a decision from a maintainer
    needs-designNeeds further design before implementation
    on Feb 19, 2025
  6. MichaReiser commented on Feb 19, 2025

    @MichaReiser
    Member

    Ha, @dylwil3 I was just about to write the same.

    Red Knot implements your desired behavior (see #15046 (comment)) but I'd prefer to keep the behavior of noqa consistent with flake8. I'd have to check what behavior flake8 uses. Either way, this is a rather significant change.

    There are some trade offs involved with matching both the start and end of the range (see red knot PR) and the main mitigation is to strictly use error codes.

  7. brandonchinn178 commented on Jul 11, 2025

    @brandonchinn178

    Not sure how easy it would be to detect, but I would actually argue that this should not be a violation of SIM103. The spirit of SIM103 is to replace simple if checks with a boolean expression, but fall through boolean checks is a very common idiom that should be allowed

    # GOOD - easy to read and extend
    def check(s: str) -> bool:
        if s.startswith("/"):
            return True
    
        if s.endswith("/"):
            return False
    
        # TODO: more guards will be added in the future
    
        return True
    
    # BAD - difficult to read or extend
    def check(s: str) -> bool:
        return s.startswith("/") or not s.endswith("/")

    The original ask would be a decent mitigation for this, but IMO a better resolution would be to exclude these situations, if possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-decisionAwaiting a decision from a maintainerneeds-designNeeds further design before implementationsuppressionRelated to supression of violations e.g. noqa

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions