Skip to content

Commit 26d104d

Browse files
authored
Fixes #3413 (#3431)
1 parent 8c0d6de commit 26d104d

3 files changed

Lines changed: 55 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Semantic versioning in our case means:
3131
- Fixes `WPS115` false-positive on `StrEnum`, `IntEnum`, `IntFlag` attributes, #3381
3232
- Fixes `WPS432`, now it ignores magic numbers in `Literal`, #3397
3333
- Fixes `WPS466` for generic type specifications `MyClassDecorator[T]`, #3417
34+
- Fixes `WPS212` to ignore nested classes and functions when counting `return` statements, #3413
3435

3536
Due to PEP-695, it's now allowed to use `[]` in the decorator only for `python3.12+`.
3637

tests/test_visitors/test_ast/test_complexity/test_function/test_returns.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,30 @@ def function():
1717
"""
1818

1919
function_with_nested_function_and_returns = """
20-
def function(): # has two returns
20+
def function(): # has one return and a nested function
2121
def factory(): # has one return
2222
return 1
2323
return factory
2424
"""
2525

26+
function_with_nested_class_and_returns = """
27+
def function(): # has one return (we do not count returns in nested objects)
28+
def inner_function(x):
29+
return x
30+
class Factory:
31+
def method(self):
32+
return 1
33+
return inner_function(Factory())
34+
"""
35+
2636

2737
@pytest.mark.parametrize(
2838
'code',
2939
[
3040
function_without_returns,
3141
function_with_returns,
3242
function_with_nested_function_and_returns,
43+
function_with_nested_class_and_returns,
3344
],
3445
)
3546
def test_returns_correct_count(
@@ -52,7 +63,6 @@ def test_returns_correct_count(
5263
'code',
5364
[
5465
function_with_returns,
55-
function_with_nested_function_and_returns,
5666
],
5767
)
5868
def test_returns_wrong_count(
@@ -72,3 +82,27 @@ def test_returns_wrong_count(
7282

7383
assert_errors(visitor, [TooManyReturnsViolation])
7484
assert_error_text(visitor, '2', option_values.max_returns)
85+
86+
87+
@pytest.mark.parametrize(
88+
'code',
89+
[
90+
function_with_nested_function_and_returns,
91+
function_with_nested_class_and_returns,
92+
],
93+
)
94+
def test_returns_in_nested_objects_correct_count(
95+
assert_errors,
96+
parse_ast_tree,
97+
options,
98+
code,
99+
mode,
100+
):
101+
"""Testing that returns in nested functions and classes are not counted."""
102+
tree = parse_ast_tree(mode(code))
103+
104+
option_values = options(max_returns=1)
105+
visitor = FunctionComplexityVisitor(option_values, tree=tree)
106+
visitor.run()
107+
108+
assert_errors(visitor, [])

wemake_python_styleguide/visitors/ast/complexity/function.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,8 @@ def _update_variables(
8888

8989
function_variables.append(variable_def.id)
9090

91-
def _check_sub_node(
92-
self,
93-
node: AnyFunctionDef,
94-
sub_node: ast.AST,
95-
) -> None:
96-
if isinstance(sub_node, ast.Name) and isinstance(
97-
sub_node.ctx,
98-
ast.Store,
99-
):
100-
self._update_variables(node, sub_node)
101-
91+
def _update_counters(self, node: AnyFunctionDef, sub_node: ast.AST) -> None:
92+
"""Updates statement counters for the given node."""
10293
error_counters: _NodeTypeHandler = {
10394
ast.Return: self.metrics.returns,
10495
ast.Expr: self.metrics.expressions,
@@ -109,8 +100,24 @@ def _check_sub_node(
109100

110101
for types, counter in error_counters.items():
111102
if isinstance(sub_node, types):
103+
# We do not count returns in nested functions and classes
104+
if types is ast.Return and get_context(sub_node) is not node:
105+
continue
112106
counter[node] += 1
113107

108+
def _check_sub_node(
109+
self,
110+
node: AnyFunctionDef,
111+
sub_node: ast.AST,
112+
) -> None:
113+
if isinstance(sub_node, ast.Name) and isinstance(
114+
sub_node.ctx,
115+
ast.Store,
116+
):
117+
self._update_variables(node, sub_node)
118+
119+
self._update_counters(node, sub_node)
120+
114121

115122
@final
116123
@alias(

0 commit comments

Comments
 (0)