Skip to content

Comments

fix: WPS457 false positive when while True is inside try/except#3604

Open
stakeswky wants to merge 6 commits intowemake-services:masterfrom
stakeswky:fix/wps457-while-in-try-except
Open

fix: WPS457 false positive when while True is inside try/except#3604
stakeswky wants to merge 6 commits intowemake-services:masterfrom
stakeswky:fix/wps457-while-in-try-except

Conversation

@stakeswky
Copy link

Fixes #3603

Problem

WPS457 (InfiniteWhileLoopViolation) was incorrectly triggered when a while True loop was wrapped in a try/except block:

def factory() -> Iterator[bytes]:
    try:
        while True:
            yield async_to_sync(async_anext)(iterator)
    except StopAsyncIteration:
        pass

This is not an infinite loop — the except handler provides a clear termination path. The previous implementation only checked for break/raise/return/ExceptHandler inside the while body, but missed the case where the enclosing try/except itself acts as the exit mechanism.

Fix

Added is_in_try_except() helper in wemake_python_styleguide/logic/tree/loops.py that walks up the AST parent chain to detect if the while node is directly inside a try block that has at least one except handler.

In _check_infinite_while_loop, if is_in_try_except() returns True, the violation is suppressed.

Note: A try/finally without except still triggers WPS457, since finally does not catch exceptions and cannot terminate the loop.

Tests

Added three new correct cases (correct_while8/9/10) covering the reported pattern, and one new wrong case (wrong_while3) for try/finally without except.

@codecov
Copy link

codecov bot commented Feb 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (bf05af9) to head (b2813ca).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##            master     #3604   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          368       368           
  Lines        12291     12302   +11     
  Branches       853       854    +1     
=========================================
+ Hits         12291     12302   +11     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Member

@sobolevn sobolevn left a comment

Choose a reason for hiding this comment

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

Thanks! If you are using AI, please, double check the produced results :)


# Correct: while inside try/except

correct_while8 = """
Copy link
Member

Choose a reason for hiding this comment

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

Please, move correct examples to correct examples' section.

Copy link
Author

Choose a reason for hiding this comment

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

Done — moved correct examples up to the correct section.

pass
"""

correct_while9 = """
Copy link
Member

Choose a reason for hiding this comment

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

All these examples are the same from the AST's point of view.

Please, remove duplicates.

Copy link
Author

Choose a reason for hiding this comment

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

Good point. Removed the duplicates — kept one simple case (correct_while9) and replaced the other with a nested variant (correct_while10) so they're structurally distinct.


def is_in_try_except(node: ast.AST) -> bool:
"""Checks whether a node is directly inside a ``try/except`` block."""
parent = get_parent(node)
Copy link
Member

Choose a reason for hiding this comment

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

Why not using get_closest_parent()?

Copy link
Author

Choose a reason for hiding this comment

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

Refactored to use get_closest_parent() — much cleaner. Thanks for the pointer.

"""

correct_while10 = """
try:
Copy link
Member

Choose a reason for hiding this comment

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

Please, add more nesting in the tests. But, keep them minimal.

Copy link
Author

Choose a reason for hiding this comment

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

Added correct_while10 with nested try/except inside the while loop to cover deeper nesting, kept it minimal.

User and others added 2 commits February 23, 2026 14:34
- Replace manual while-loop in is_in_try_except() with get_closest_parent()
  to find the nearest Try or scope-boundary parent
- Move correct_while8/9/10 to the correct examples section
- Remove duplicate correct_while9 (same AST as correct_while8)
- Add correct_while10 with nested try/except for more nesting coverage
Copy link
Author

@stakeswky stakeswky left a comment

Choose a reason for hiding this comment

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

Thanks for the detailed review! I've addressed all four points:

  1. get_closest_parent(): Refactored is_in_try_except() to use walk.get_closest_parent(node, (*_SCOPE_BOUNDARIES, ast.Try)) — finds the nearest Try or scope-boundary parent in one call, then checks if it's a Try with handlers.

  2. Correct examples section: Moved correct_while8/9/10 above the # Do raise: section where they belong.

  3. Removed duplicate: correct_while9 (same AST as correct_while8 — both are def fn(): try: while True: yield; except: pass) has been removed and replaced with a simpler top-level try/except case.

  4. More nesting: Added correct_while10 with a nested try/except inside the while True body, inside an outer try/except.

Comment on lines 112 to 116
try:
while True:
yield some()
except StopIteration:
pass
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
try:
while True:
yield some()
except StopIteration:
pass
try:
if some:
while True:
yield some
except StopIteration:
pass

Copy link
Author

Choose a reason for hiding this comment

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

Applied your suggestion — replaced with try: if some: while True: yield some; except StopIteration: pass to test deeper nesting.

from wemake_python_styleguide.logic.walk import tree as walk
from wemake_python_styleguide.types import AnyLoop, AnyNodes

_SCOPE_BOUNDARIES = (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)
Copy link
Member

Choose a reason for hiding this comment

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

You don't need a variable for a single use of values.

Copy link
Author

Choose a reason for hiding this comment

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

Done — removed _SCOPE_BOUNDARIES and inlined the tuple directly in the get_closest_parent() call.

handle()
"""

correct_while10 = """
Copy link
Member

Choose a reason for hiding this comment

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

you never test try being further in hierarchy then scope boundaries.

Copy link
Author

Choose a reason for hiding this comment

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

Added wrong_while4: a while True inside a function that is itself inside a try/except — the scope boundary (function def) should block the try from exempting the loop, so it raises InfiniteWhileLoopViolation. Included in test_wrong_while_loops_with_try.

- Remove _SCOPE_BOUNDARIES variable, inline tuple directly in get_closest_parent() call
- Replace correct_while8 with sobolevn's suggestion: try/if/while nesting
- Add wrong_while4: try/except outside a function boundary should not exempt the inner while
- Include wrong_while4 in test_wrong_while_loops_with_try parametrize
handle()
"""

# Wrong: try/except is outside a scope boundary, should not exempt the while
Copy link
Member

Choose a reason for hiding this comment

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

This comment is not required.

Copy link
Author

Choose a reason for hiding this comment

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

Removed.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WPS457 false positive with while in try / except

2 participants