Skip to content

[pylint] Implement too-many-try-statements (W0717)#23970

Merged
ntBre merged 1 commit into
astral-sh:mainfrom
neutrinoceros:w0717
May 15, 2026
Merged

[pylint] Implement too-many-try-statements (W0717)#23970
ntBre merged 1 commit into
astral-sh:mainfrom
neutrinoceros:w0717

Conversation

@neutrinoceros

Copy link
Copy Markdown
Contributor

Summary

Close #23810

Opening as a draft mostly to signal that I'm getting this going. At the time of opening, and as far as I can tell, this compiles correctly locally, but it's missing tests and docs.

I also have a question I would like to ask @ntBre at this early stage of the PR: it seemed to me that the logic from too_many_statements::num_statements could be entirely re-used, and I didn't want to duplicate it, so I moved it up the module tree to ruff_linter::rules::pylint::helpers. Is this the prefered approach, or is there a better way I should implement this code re-use ?

Test Plan

@ntBre

ntBre commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

I also have a question I would like to ask @ntBre at this early stage of the PR: it seemed to me that the logic from too_many_statements::num_statements could be entirely re-used, and I didn't want to duplicate it, so I moved it up the module tree to ruff_linter::rules::pylint::helpers. Is this the prefered approach, or is there a better way I should implement this code re-use ?

That sounds perfect!

This is looking good from a very quick skim. Let me know if you need any help! I'll approve the CI runs in case it's helpful.

@ntBre ntBre added rule Implementing or modifying a lint rule preview Related to preview mode features labels Mar 16, 2026
@astral-sh-bot

astral-sh-bot Bot commented Mar 16, 2026

Copy link
Copy Markdown

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

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

DisnakeDev/disnake (+25 -0 violations, +0 -0 fixes)

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

+ disnake/__main__.py:297:5: PLW0717 Try clause contains too many statements (8 > 5)
+ disnake/__main__.py:328:5: PLW0717 Try clause contains too many statements (17 > 5)
+ disnake/abc.py:788:9: PLW0717 Try clause contains too many statements (6 > 5)
+ disnake/client.py:1109:13: PLW0717 Try clause contains too many statements (6 > 5)
+ disnake/ext/commands/base_core.py:680:9: PLW0717 Try clause contains too many statements (12 > 5)
+ disnake/ext/commands/cog.py:746:13: PLW0717 Try clause contains too many statements (6 > 5)
+ disnake/ext/commands/cog.py:830:9: PLW0717 Try clause contains too many statements (25 > 5)
+ disnake/ext/commands/context.py:375:9: PLW0717 Try clause contains too many statements (7 > 5)
+ disnake/ext/commands/core.py:1098:9: PLW0717 Try clause contains too many statements (11 > 5)
+ disnake/ext/commands/core.py:791:9: PLW0717 Try clause contains too many statements (7 > 5)
... 15 additional changes omitted for project

aiven/aiven-client (+1 -0 violations, +0 -0 fixes)

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

+ aiven/client/cli.py:3557:9: PLW0717 Try clause contains too many statements (16 > 5)

PlasmaPy/PlasmaPy (+10 -0 violations, +0 -0 fixes)

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

+ src/plasmapy/particles/_parsing.py:196:9: PLW0717 Try clause contains too many statements (8 > 5)
+ src/plasmapy/particles/ionization_state.py:283:9: PLW0717 Try clause contains too many statements (6 > 5)
+ src/plasmapy/particles/ionization_state.py:470:9: PLW0717 Try clause contains too many statements (18 > 5)
+ src/plasmapy/particles/ionization_state_collection.py:169:9: PLW0717 Try clause contains too many statements (9 > 5)
+ src/plasmapy/particles/ionization_state_collection.py:202:9: PLW0717 Try clause contains too many statements (8 > 5)
+ src/plasmapy/particles/nuclear.py:206:13: PLW0717 Try clause contains too many statements (13 > 5)
+ src/plasmapy/particles/particle_class.py:803:9: PLW0717 Try clause contains too many statements (7 > 5)
+ src/plasmapy/particles/particle_class.py:828:9: PLW0717 Try clause contains too many statements (13 > 5)
+ tests/utils/_pytest_helpers/test_pytest_helpers.py:139:5: PLW0717 Try clause contains too many statements (6 > 5)
+ tools/export_ionization_energy.py:176:5: PLW0717 Try clause contains too many statements (24 > 5)

apache/airflow (+471 -0 violations, +0 -0 fixes)

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

+ airflow-core/hatch_build.py:90:9: PLW0717 Try clause contains too many statements (10 > 5)
+ airflow-core/src/airflow/api/common/airflow_health.py:40:5: PLW0717 Try clause contains too many statements (6 > 5)
+ airflow-core/src/airflow/api/common/airflow_health.py:51:5: PLW0717 Try clause contains too many statements (8 > 5)
+ airflow-core/src/airflow/api/common/airflow_health.py:64:5: PLW0717 Try clause contains too many statements (8 > 5)
+ airflow-core/src/airflow/api_fastapi/auth/managers/simple/simple_auth_manager.py:187:9: PLW0717 Try clause contains too many statements (16 > 5)
+ airflow-core/src/airflow/api_fastapi/auth/managers/simple/simple_auth_manager.py:189:17: PLW0717 Try clause contains too many statements (11 > 5)
+ airflow-core/src/airflow/api_fastapi/auth/managers/simple/utils.py:32:5: PLW0717 Try clause contains too many statements (7 > 5)
+ airflow-core/src/airflow/api_fastapi/auth/middlewares/refresh_token.py:49:9: PLW0717 Try clause contains too many statements (17 > 5)
+ airflow-core/src/airflow/api_fastapi/auth/middlewares/refresh_token.py:51:17: PLW0717 Try clause contains too many statements (6 > 5)
+ airflow-core/src/airflow/api_fastapi/auth/tokens.py:146:9: PLW0717 Try clause contains too many statements (9 > 5)
+ airflow-core/src/airflow/api_fastapi/core_api/routes/public/connections.py:243:5: PLW0717 Try clause contains too many statements (11 > 5)
+ airflow-core/src/airflow/api_fastapi/core_api/services/public/connections.py:109:9: PLW0717 Try clause contains too many statements (13 > 5)
... 459 additional changes omitted for project

apache/superset (+301 -0 violations, +0 -0 fixes)

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

+ RELEASING/changelog.py:90:9: PLW0717 Try clause contains too many statements (6 > 5)
+ docs/scripts/extract_custom_errors.py:147:5: PLW0717 Try clause contains too many statements (13 > 5)
+ superset-extensions-cli/src/superset_extensions_cli/cli.py:71:5: PLW0717 Try clause contains too many statements (8 > 5)
+ superset/app.py:124:9: PLW0717 Try clause contains too many statements (11 > 5)
+ superset/app.py:162:9: PLW0717 Try clause contains too many statements (10 > 5)
+ superset/app.py:54:5: PLW0717 Try clause contains too many statements (15 > 5)
+ superset/cli/export_example.py:187:5: PLW0717 Try clause contains too many statements (10 > 5)
+ superset/commands/dashboard/export_example.py:254:5: PLW0717 Try clause contains too many statements (23 > 5)
+ superset/commands/database/sync_permissions.py:140:13: PLW0717 Try clause contains too many statements (8 > 5)
+ superset/commands/database/sync_permissions.py:307:9: PLW0717 Try clause contains too many statements (10 > 5)
... 291 additional changes omitted for project

aws/aws-sam-cli (+106 -0 violations, +0 -0 fixes)

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

+ samcli/cli/global_config.py:239:9: PLW0717 Try clause contains too many statements (9 > 5)
+ samcli/commands/build/build_context.py:262:9: PLW0717 Try clause contains too many statements (25 > 5)
+ samcli/commands/deploy/deploy_context.py:250:13: PLW0717 Try clause contains too many statements (11 > 5)
+ samcli/commands/init/init_templates.py:284:9: PLW0717 Try clause contains too many statements (8 > 5)
+ samcli/commands/local/cli_common/invoke_context.py:370:9: PLW0717 Try clause contains too many statements (10 > 5)
+ samcli/commands/local/start_api/cli.py:241:5: PLW0717 Try clause contains too many statements (7 > 5)
+ samcli/commands/remote/callback/fail/cli.py:85:5: PLW0717 Try clause contains too many statements (6 > 5)
+ samcli/commands/remote/callback/heartbeat/cli.py:69:5: PLW0717 Try clause contains too many statements (6 > 5)
+ samcli/commands/remote/callback/succeed/cli.py:72:5: PLW0717 Try clause contains too many statements (6 > 5)
+ samcli/commands/remote/execution/get/cli.py:73:5: PLW0717 Try clause contains too many statements (6 > 5)
... 96 additional changes omitted for project

bokeh/bokeh (+22 -0 violations, +0 -0 fixes)

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

+ examples/server/app/spectrogram/audio.py:16:1: PLW0717 Try clause contains too many statements (14 > 5)
+ examples/server/app/spectrogram/audio.py:30:13: PLW0717 Try clause contains too many statements (6 > 5)
+ release/build.py:114:5: PLW0717 Try clause contains too many statements (13 > 5)
+ release/checks.py:112:5: PLW0717 Try clause contains too many statements (12 > 5)
+ release/deploy.py:57:5: PLW0717 Try clause contains too many statements (9 > 5)
+ release/remote.py:53:5: PLW0717 Try clause contains too many statements (7 > 5)
+ release/ui.py:26:1: PLW0717 Try clause contains too many statements (10 > 5)
+ setup.py:25:1: PLW0717 Try clause contains too many statements (8 > 5)
+ src/bokeh/io/notebook.py:110:9: PLW0717 Try clause contains too many statements (7 > 5)
+ src/bokeh/server/callbacks.py:214:9: PLW0717 Try clause contains too many statements (6 > 5)
... 12 additional changes omitted for project

freedomofpress/securedrop (+22 -0 violations, +0 -0 fixes)

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

+ install_files/ansible-base/roles/tails-config/files/securedrop_init.py:155:9: PLW0717 Try clause contains too many statements (11 > 5)
+ molecule/testinfra/app/test_redis.py:64:9: PLW0717 Try clause contains too many statements (11 > 5)
+ molecule/testinfra/app/test_smoke.py:85:9: PLW0717 Try clause contains too many statements (6 > 5)
+ molecule/testinfra/common/test_grsecurity.py:154:5: PLW0717 Try clause contains too many statements (9 > 5)
+ securedrop/journalist_app/admin.py:157:13: PLW0717 Try clause contains too many statements (7 > 5)
+ securedrop/journalist_app/api.py:90:9: PLW0717 Try clause contains too many statements (9 > 5)
+ securedrop/journalist_app/col.py:147:9: PLW0717 Try clause contains too many statements (10 > 5)
+ securedrop/journalist_app/main.py:147:9: PLW0717 Try clause contains too many statements (6 > 5)
+ securedrop/journalist_app/utils.py:168:9: PLW0717 Try clause contains too many statements (13 > 5)
+ securedrop/manage.py:433:5: PLW0717 Try clause contains too many statements (9 > 5)
... 12 additional changes omitted for project

fronzbot/blinkpy (+16 -0 violations, +0 -0 fixes)

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

+ blinkpy/auth.py:122:9: PLW0717 Try clause contains too many statements (7 > 5)
+ blinkpy/auth.py:148:9: PLW0717 Try clause contains too many statements (7 > 5)
+ blinkpy/auth.py:259:9: PLW0717 Try clause contains too many statements (10 > 5)
+ blinkpy/blinkpy.py:222:9: PLW0717 Try clause contains too many statements (9 > 5)
+ blinkpy/blinkpy.py:246:9: PLW0717 Try clause contains too many statements (9 > 5)
+ blinkpy/blinkpy.py:276:9: PLW0717 Try clause contains too many statements (9 > 5)
+ blinkpy/camera.py:329:9: PLW0717 Try clause contains too many statements (16 > 5)
+ blinkpy/livestream.py:153:9: PLW0717 Try clause contains too many statements (6 > 5)
+ blinkpy/livestream.py:180:9: PLW0717 Try clause contains too many statements (28 > 5)
+ blinkpy/livestream.py:269:9: PLW0717 Try clause contains too many statements (14 > 5)
... 6 additional changes omitted for project

... Truncated remaining completed project reports due to GitHub comment length restrictions

Changes by rule (1 rules affected)

code total + violation - violation + fix - fix
PLW0717 1921 1921 0 0 0

@amyreese

Copy link
Copy Markdown
Contributor

Not being familiar with the original rule, both the name and the diagnostic message seem very confusing/misleading to me. "Too many try statements" feels like it's complaining about how many separate try/except blocks there are, not how many statements are within a single try block.

I'd suggest something more like "Too many statements in try block" or similar. Looks like pylint uses "Try clause contains too many statements".

@neutrinoceros

Copy link
Copy Markdown
Contributor Author

I agree, this name is terrible. Is there a way I could rename the rule but keep an alias to the original name for discoverability or should I just clarify the documentation for it ?

@amyreese

Copy link
Copy Markdown
Contributor

Since this is a new-to-Ruff rule, simply rename the rule struct. I don't think we are tied to the existing rule names from other linters (though we do preserve the existing rule code).

@neutrinoceros neutrinoceros force-pushed the w0717 branch 2 times, most recently from da05928 to 304c358 Compare March 17, 2026 12:21
@neutrinoceros

neutrinoceros commented Mar 17, 2026

Copy link
Copy Markdown
Contributor Author

Update:

  • added example
  • updated config schema
  • lint+formatting
  • renamed the struct to TooManyStatementsInTryClause (is it the source of truth for the rule's name as it appears in ruff's docs ?)

I'll get started on tests promptly.

@neutrinoceros

neutrinoceros commented Mar 17, 2026

Copy link
Copy Markdown
Contributor Author

So I was planning to mimick too-many-statements' test module, and realized that all of its tests were actually checking the now-helper function num_statements, so I moved these tests to stay close to the definition, and now I'm not sure wether I need to add new tests to either rule. Meanwhile, I manually checked that the new rule behaved as expected on the example I added to docs with

# t.py
def random_ratio() -> float:
    try:
        a = randint(-100, 100)
        b = randint(-100, 100)
        c = randint(-100, 100)
        d = randint(-100, 100)
        scale = randint(1, 5)
        res = scale * (a + b) / (c + d)
    except ZeroDivisionError:
        return random_ratio()
    else:
        return res
❯ ruff check --preview --select PLW0717 t.py
error[PLW0717]: Try clause contains too many statements (6 > 5)
  --> t.py:3:5
   |
 1 |   # t.py
 2 |   def random_ratio() -> float:
 3 | /     try:
 4 | |         a = randint(-100, 100)
 5 | |         b = randint(-100, 100)
 6 | |         c = randint(-100, 100)
 7 | |         d = randint(-100, 100)
 8 | |         scale = randint(1, 5)
 9 | |         res = scale * (a + b) / (c + d)
10 | |     except ZeroDivisionError:
11 | |         return random_ratio()
12 | |     else:
13 | |         return res
   | |__________________^
   |

Found 1 error.

The only problem I see here is that the generated error points to the wrong place, but I don't know how to fix that, please advise.
Other than that, and as dfar as I can tell, everything is now ready for review.

@neutrinoceros neutrinoceros marked this pull request as ready for review March 17, 2026 14:36
@astral-sh-bot astral-sh-bot Bot requested a review from ntBre March 17, 2026 14:36

@ntBre ntBre left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thank you! I only had a couple of small suggestions, but this looks great to me overall. I think moving the tests along with the helper function makes sense.

Let's make sure we look through the ecosystem results (I'll approve the workflow again) too. I expect this to be a pretty noisy/pedantic rule with a lot of hits, but it should also be accurate since we're just reusing num_statements. Just good to double check.

Comment thread crates/ruff_linter/src/rules/pylint/rules/too_many_try_statements.rs Outdated
Comment thread crates/ruff_linter/src/rules/pylint/rules/too_many_try_statements.rs Outdated
Comment thread crates/ruff_linter/src/rules/pylint/rules/too_many_try_statements.rs Outdated
@neutrinoceros

Copy link
Copy Markdown
Contributor Author

thanks @ntBre !
Here's another question: as far as I understand, pylint's own default value for max-try-statements is 1, while I arbitrarily picked 5 here because I feared a smaller number would generate an inordinate amount of noise and would hinder adoption. Is this practice acceptable and is there some usual way to communicate it through docs ?

@ntBre

ntBre commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

thanks @ntBre ! Here's another question: as far as I understand, pylint's own default value for max-try-statements is 1, while I arbitrarily picked 5 here because I feared a smaller number would generate an inordinate amount of noise and would hinder adoption. Is this practice acceptable and is there some usual way to communicate it through docs ?

I noticed that too, but I think I'm happy with the more conservative default. It might be easiest to note this in the example for the configuration option. Something like:

# use the default from pylint
max-try-statements = 1

@neutrinoceros

Copy link
Copy Markdown
Contributor Author

Gotcha. Will push this in a minute

@neutrinoceros

Copy link
Copy Markdown
Contributor Author

@ntBre any update ?

@neutrinoceros

Copy link
Copy Markdown
Contributor Author

rebased to refresh CI

@ntBre ntBre left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks again and sorry for the delay. I had a couple more really small nits, and we need to add some tests. I didn't notice that before. It looks like other pylint rules are here:

#[test_case(Rule::LenTest, Path::new("len_as_condition.py"))]
#[test_case(Rule::MissingMaxsplitArg, Path::new("missing_maxsplit_arg.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {

Comment thread crates/ruff_linter/src/rules/pylint/settings.rs
Comment thread crates/ruff_linter/src/rules/pylint/rules/too_many_try_statements.rs Outdated
@neutrinoceros

Copy link
Copy Markdown
Contributor Author

Tests added, and took your suggestion into account. Thank you !

Comment thread crates/ruff_linter/src/rules/pylint/mod.rs Outdated
@neutrinoceros neutrinoceros force-pushed the w0717 branch 2 times, most recently from c13704d to bc60b9f Compare May 13, 2026 06:33
@neutrinoceros

Copy link
Copy Markdown
Contributor Author

I just rebased the branch again, but the only change was the inclusion of test files I forgot to include last time.

Before this is considered for merge though, I still would like to ask whether it's okay to rename the public parameter for clarity (max-try-statements -> max-statements-in-try(-clause)), instead of adopting pylint's naming there. I could of course mention the pylint equivalent to improve discoverability.

@ntBre

ntBre commented May 15, 2026

Copy link
Copy Markdown
Contributor

I just rebased the branch again, but the only change was the inclusion of test files I forgot to include last time.

Before this is considered for merge though, I still would like to ask whether it's okay to rename the public parameter for clarity (max-try-statements -> max-statements-in-try(-clause)), instead of adopting pylint's naming there. I could of course mention the pylint equivalent to improve discoverability.

Thanks! Don't worry about the cargo shear check. I think I'll need to close and reopen the PR to update the merge base and pull in a fix from main (unless you end up pushing again).

I do think max-statements-in-try sounds better. That had crossed my mind before but I thought max-try-statements was consistent with our other pylint options. I see now that the semantics are a bit different though and seem to warrant the slightly different name. Feel free to rename the option if you prefer it too, and then I think this is good to go!

@neutrinoceros

Copy link
Copy Markdown
Contributor Author

Nice. I'll push again in a couple minutes then

@neutrinoceros

Copy link
Copy Markdown
Contributor Author

(I think I overestimated my travel laptop's capacity to run tests in ordinate time, but the patch is still coming)

@neutrinoceros

Copy link
Copy Markdown
Contributor Author

I was also wondering if I should rename any files, or the rule itself, as was discussed early on, but I can't tell what's the actual source of truth in this aspect.
Fort reference, I would rename the rule to too-many-statements-in-try, but I don't know if I'll be able to do it myself in the following days.

@ntBre

ntBre commented May 15, 2026

Copy link
Copy Markdown
Contributor

The source of truth for the rule name is the Violation struct name, which is currently TooManyStatementsInTryClause. I think that sounds good, if it's okay with you.

I'm also happy to push any snapshot updates/test fixes if needed :) I'll kick off CI again.

@neutrinoceros

Copy link
Copy Markdown
Contributor Author

I like this name even more indeed. Do feel free to push to my branch if anything trivial is still missing or incorrect !

@ntBre ntBre left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me! Thanks again for all of your work here and for your patience with reviews!

@ntBre ntBre merged commit 2072f3f into astral-sh:main May 15, 2026
45 checks passed
thejchap pushed a commit to thejchap/ruff that referenced this pull request May 23, 2026
…3970)

## Summary

Close astral-sh#23810

Opening as a draft mostly to signal that I'm getting this going. At the
time of opening, and as far as I can tell, this *compiles* correctly
locally, but it's missing tests and docs.

I also have a question I would like to ask @ntBre at this early stage of
the PR: it seemed to me that the logic from
`too_many_statements::num_statements` could be entirely re-used, and I
didn't want to duplicate it, so I moved it up the module tree to
`ruff_linter::rules::pylint::helpers`. Is this the prefered approach, or
is there a better way I should implement this code re-use ?

## Test Plan
anishgirianish pushed a commit to anishgirianish/ruff that referenced this pull request May 28, 2026
…3970)

## Summary

Close astral-sh#23810

Opening as a draft mostly to signal that I'm getting this going. At the
time of opening, and as far as I can tell, this *compiles* correctly
locally, but it's missing tests and docs.

I also have a question I would like to ask @ntBre at this early stage of
the PR: it seemed to me that the logic from
`too_many_statements::num_statements` could be entirely re-used, and I
didn't want to duplicate it, so I moved it up the module tree to
`ruff_linter::rules::pylint::helpers`. Is this the prefered approach, or
is there a better way I should implement this code re-use ?

## Test Plan
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.

implementing pylint W0717 (too-many-try-statements)

3 participants