Skip to content

[ruff] New rule useless-finally (RUF072)#24165

Merged
MichaReiser merged 2 commits intoastral-sh:mainfrom
seroperson:i19158-useless-finally
Mar 25, 2026
Merged

[ruff] New rule useless-finally (RUF072)#24165
MichaReiser merged 2 commits intoastral-sh:mainfrom
seroperson:i19158-useless-finally

Conversation

@seroperson
Copy link
Copy Markdown
Contributor

@seroperson seroperson commented Mar 24, 2026

Closes #19158

Implements the useless_finally rule (RUF072), which detects useless finally blocks that only contain pass or ....

It handles two cases:

  • try/except/finally: pass - the finally clause is removed, leaving a valid try/except
  • bare try/finally: pass: the entire try/finally is unwrapped, the try body is dedented to replace the whole statement

Fix is skipped when comments are present in or around the finally block.

It complements with existing rules like RUF047 (needless-else) and SIM105 (suppressible-exception). It case of SIM105 it also unblocks this rule, as currently SIM105 got skipped if finally has any body at all (even just pass).

Test Plan

  • RUF072.py - main rule test with error cases and non-error.
  • useless_finally_and_needless_else - test function, which checks how RUF047 and RUF072 work together on the same try statement.
  • useless_finally_and_suppressible_exception - test function, which checks how RUF072 and SIM105 work together.

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Mar 24, 2026

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+2 -0 violations, +0 -0 fixes in 2 projects; 54 projects unchanged)

langchain-ai/langchain (+1 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ libs/langchain_v1/tests/unit_tests/agents/middleware/implementations/test_shell_tool.py:451:5: RUF072 [*] Empty `finally` clause

pytest-dev/pytest (+1 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ testing/test_link_resolve.py:49:5: RUF072 Empty `finally` clause

Changes by rule (1 rules affected)

code total + violation - violation + fix - fix
RUF072 2 2 0 0 0

Formatter (stable)

✅ ecosystem check detected no format changes.

Formatter (preview)

✅ ecosystem check detected no format changes.

@seroperson seroperson force-pushed the i19158-useless-finally branch from 7b06e01 to 387d670 Compare March 24, 2026 23:15
Copy link
Copy Markdown
Member

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, thank you. I only have a few smaller questions

/// - [`useless-try-except`][TRY203]: Flags `try/except` that only re-raises
#[derive(ViolationMetadata)]
#[violation_metadata(preview_since = "0.15.7")]
pub(crate) struct UselessFinally;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you say more on why you named the rule UselessFinally over NeedlessFinally (I'm not a native English speaker, so there might be a very good reason that isn't clear to me :)). I'm mainly asking because we have needless-else.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the question I've been asking myself. The issue was titled "useless finally", so I started with that naming from the very beginning. Maybe there is some distinction between useless and needless, but I'm not sure whether existing rules follow it. Actually, there are only two needless rules (needless_bool, needless_else) and a lot more useless (useless_comparison, useless_expression, useless_return, useless_if_else, useless_try_except etc).

I can rename it if necessary.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with useless, but we should rename needless_if to unnecessary_if.

@MichaReiser MichaReiser added the rule Implementing or modifying a lint rule label Mar 25, 2026
@seroperson seroperson requested a review from MichaReiser March 25, 2026 11:10
@MichaReiser MichaReiser merged commit ceddca7 into astral-sh:main Mar 25, 2026
46 checks passed
@MichaReiser MichaReiser added the preview Related to preview mode features label Mar 25, 2026
carljm added a commit that referenced this pull request Mar 25, 2026
* main:
  [ty] make `test-case` a dev-dependency (#24187)
  [ty] implement cycle normalization for more types to prevent too-many-cycle panics (#24061)
  [ty] Silence all diagnostics in unreachable code (#24179)
  [ty] Intern `InferableTypeVars` (#24161)
  Implement unnecessary-if (RUF050) (#24114)
  Recognize `Self` annotation and `self` assignment in SLF001 (#24144)
  Bump the npm version before publish (#24178)
  [ty] Disallow Self in metaclass and static methods (#23231)
  Use trusted publishing for NPM packages (#24171)
  [ty] Respect non-explicitly defined dataclass params (#24170)
  Add RUF072: warn when using  operator on an f-string (#24162)
  [ty] Check return type of generator functions (#24026)
  Implement useless-finally (RUF-072) (#24165)
  [ty] Add test for a dataclass with a default field converter (#24169)
  [ty] Dataclass field converters (#23088)
  [flake8-bandit] Treat sys.executable as trusted input in S603 (#24106)
  [ty] Add support for `typing.Concatenate` (#23689)
  `ASYNC115`: autofix to use full qualified `anyio.lowlevel` import (#24166)
  [ty] Disallow read-only fields in TypedDict updates (#24128)
  Speed up diagnostic rendering (#24146)
@amyreese amyreese changed the title Implement useless-finally (RUF-072) [ruff] New rule useless-finally (RUF072) Mar 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview Related to preview mode features rule Implementing or modifying a lint rule

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New rule: useless finally

3 participants