From aa887d5a1d92a8656d4407a6d2e942daadce9f15 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 28 Jun 2023 22:00:06 -0400 Subject: [PATCH] Use "manual" fixability for E731 in shadowed context (#5430) ## Summary This PR makes E731 a "manual" fix in one other context: when the lambda is shadowing another variable in the scope. Function declarations (with shadowing) cause issues for type checkers, and so rewriting an annotation, e.g., in branches of an `if` statement can lead to failures. Closes https://github.com/astral-sh/ruff/issues/5421. --- .../test/fixtures/pycodestyle/E731.py | 160 ++++-- .../pycodestyle/rules/lambda_assignment.rs | 94 ++-- ...les__pycodestyle__tests__E731_E731.py.snap | 480 ++++++++++-------- 3 files changed, 434 insertions(+), 300 deletions(-) diff --git a/crates/ruff/resources/test/fixtures/pycodestyle/E731.py b/crates/ruff/resources/test/fixtures/pycodestyle/E731.py index 4464c0c8b9..c207c7dae9 100644 --- a/crates/ruff/resources/test/fixtures/pycodestyle/E731.py +++ b/crates/ruff/resources/test/fixtures/pycodestyle/E731.py @@ -1,51 +1,135 @@ -#: E731 -f = lambda x: 2 * x -#: E731 -f = lambda x: 2 * x -#: E731 -while False: - this = lambda y, z: 2 * x -#: E731 -f = lambda: (yield 1) -#: E731 -f = lambda: (yield from g()) -#: E731 -class F: +def scope(): + # E731 f = lambda x: 2 * x -f = object() -f.method = lambda: "Method" -f = {} -f["a"] = lambda x: x**2 -f = [] -f.append(lambda x: x**2) -f = g = lambda x: x**2 -lambda: "no-op" +def scope(): + # E731 + f = lambda x: 2 * x -# Annotated -from typing import Callable, ParamSpec -P = ParamSpec("P") +def scope(): + # E731 + while False: + this = lambda y, z: 2 * x + + +def scope(): + # E731 + f = lambda: (yield 1) + + +def scope(): + # E731 + f = lambda: (yield from g()) + + +def scope(): + # OK + f = object() + f.method = lambda: "Method" + + +def scope(): + # OK + f = {} + f["a"] = lambda x: x**2 + + +def scope(): + # OK + f = [] + f.append(lambda x: x**2) + + +def scope(): + # OK + f = g = lambda x: x**2 + + +def scope(): + # OK + lambda: "no-op" + + +class Scope: + # E731 + f = lambda x: 2 * x + + +class Scope: + from typing import Callable + + # E731 + f: Callable[[int], int] = lambda x: 2 * x + + +def scope(): + # E731 + from typing import Callable + + x: Callable[[int], int] + if True: + x = lambda: 1 + else: + x = lambda: 2 + return x + + +def scope(): + # E731 + + from typing import Callable, ParamSpec + + # ParamSpec cannot be used in this context, so do not preserve the annotation. + P = ParamSpec("P") + f: Callable[P, int] = lambda *args: len(args) + + +def scope(): + # E731 + + from typing import Callable + + f: Callable[[], None] = lambda: None + + +def scope(): + # E731 + + from typing import Callable + + f: Callable[..., None] = lambda a, b: None + + +def scope(): + # E731 + + from typing import Callable + + f: Callable[[int], int] = lambda x: 2 * x -# ParamSpec cannot be used in this context, so do not preserve the annotation. -f: Callable[P, int] = lambda *args: len(args) -f: Callable[[], None] = lambda: None -f: Callable[..., None] = lambda a, b: None -f: Callable[[int], int] = lambda x: 2 * x # Let's use the `Callable` type from `collections.abc` instead. -from collections.abc import Callable +def scope(): + # E731 -f: Callable[[str, int], str] = lambda a, b: a * b -f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b) -f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b] + from collections.abc import Callable + + f: Callable[[str, int], str] = lambda a, b: a * b -# Override `Callable` -class Callable: - pass +def scope(): + # E731 + + from collections.abc import Callable + + f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b) -# Do not copy the annotation from here on out. -f: Callable[[str, int], str] = lambda a, b: a * b +def scope(): + # E731 + + from collections.abc import Callable + + f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b] diff --git a/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs b/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs index 8cc70a26c9..8e3f4930e2 100644 --- a/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs +++ b/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs @@ -63,57 +63,69 @@ pub(crate) fn lambda_assignment( annotation: Option<&Expr>, stmt: &Stmt, ) { - if let Expr::Name(ast::ExprName { id, .. }) = target { - if let Expr::Lambda(ast::ExprLambda { args, body, .. }) = value { - let mut diagnostic = Diagnostic::new( - LambdaAssignment { - name: id.to_string(), - }, - stmt.range(), - ); + let Expr::Name(ast::ExprName { id, .. }) = target else { + return; + }; - // If the assignment is in a class body, it might not be safe - // to replace it because the assignment might be - // carrying a type annotation that will be used by some - // package like dataclasses, which wouldn't consider the - // rewritten function definition to be equivalent. - // See https://github.com/astral-sh/ruff/issues/3046 - if checker.patch(diagnostic.kind.rule()) - && !checker.semantic().scope().kind.is_class() - && !has_leading_content(stmt.start(), checker.locator) - && !has_trailing_content(stmt.end(), checker.locator) + let Expr::Lambda(ast::ExprLambda { args, body, .. }) = value else { + return; + }; + + let mut diagnostic = Diagnostic::new( + LambdaAssignment { + name: id.to_string(), + }, + stmt.range(), + ); + + if checker.patch(diagnostic.kind.rule()) { + if !has_leading_content(stmt.start(), checker.locator) + && !has_trailing_content(stmt.end(), checker.locator) + { + let first_line = checker.locator.line(stmt.start()); + let indentation = leading_indentation(first_line); + let mut indented = String::new(); + for (idx, line) in function( + id, + args, + body, + annotation, + checker.semantic(), + checker.generator(), + ) + .universal_newlines() + .enumerate() { - let first_line = checker.locator.line(stmt.start()); - let indentation = leading_indentation(first_line); - let mut indented = String::new(); - for (idx, line) in function( - id, - args, - body, - annotation, - checker.semantic(), - checker.generator(), - ) - .universal_newlines() - .enumerate() - { - if idx == 0 { - indented.push_str(&line); - } else { - indented.push_str(checker.stylist.line_ending().as_str()); - indented.push_str(indentation); - indented.push_str(&line); - } + if idx == 0 { + indented.push_str(&line); + } else { + indented.push_str(checker.stylist.line_ending().as_str()); + indented.push_str(indentation); + indented.push_str(&line); } + } + + // If the assignment is in a class body, it might not be safe to replace it because the + // assignment might be carrying a type annotation that will be used by some package like + // dataclasses, which wouldn't consider the rewritten function definition to be + // equivalent. Similarly, if the lambda is shadowing a variable in the current scope, + // rewriting it as a function declaration may break type-checking. + // See: https://github.com/astral-sh/ruff/issues/3046 + // See: https://github.com/astral-sh/ruff/issues/5421 + if (annotation.is_some() && checker.semantic().scope().kind.is_class()) + || checker.semantic().scope().has(id) + { + diagnostic.set_fix(Fix::manual(Edit::range_replacement(indented, stmt.range()))); + } else { diagnostic.set_fix(Fix::suggested(Edit::range_replacement( indented, stmt.range(), ))); } - - checker.diagnostics.push(diagnostic); } } + + checker.diagnostics.push(diagnostic); } /// Extract the argument types and return type from a `Callable` annotation. diff --git a/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E731_E731.py.snap b/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E731_E731.py.snap index 1657c1069e..440cc87ebd 100644 --- a/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E731_E731.py.snap +++ b/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E731_E731.py.snap @@ -1,284 +1,322 @@ --- source: crates/ruff/src/rules/pycodestyle/mod.rs --- -E731.py:2:1: E731 [*] Do not assign a `lambda` expression, use a `def` +E731.py:3:5: E731 [*] Do not assign a `lambda` expression, use a `def` | -1 | #: E731 -2 | f = lambda x: 2 * x - | ^^^^^^^^^^^^^^^^^^^ E731 -3 | #: E731 -4 | f = lambda x: 2 * x +1 | def scope(): +2 | # E731 +3 | f = lambda x: 2 * x + | ^^^^^^^^^^^^^^^^^^^ E731 | = help: Rewrite `f` as a `def` ℹ Suggested fix -1 1 | #: E731 -2 |-f = lambda x: 2 * x - 2 |+def f(x): - 3 |+ return 2 * x -3 4 | #: E731 -4 5 | f = lambda x: 2 * x -5 6 | #: E731 +1 1 | def scope(): +2 2 | # E731 +3 |- f = lambda x: 2 * x + 3 |+ def f(x): + 4 |+ return 2 * x +4 5 | +5 6 | +6 7 | def scope(): -E731.py:4:1: E731 [*] Do not assign a `lambda` expression, use a `def` +E731.py:8:5: E731 [*] Do not assign a `lambda` expression, use a `def` | -2 | f = lambda x: 2 * x -3 | #: E731 -4 | f = lambda x: 2 * x - | ^^^^^^^^^^^^^^^^^^^ E731 -5 | #: E731 -6 | while False: +6 | def scope(): +7 | # E731 +8 | f = lambda x: 2 * x + | ^^^^^^^^^^^^^^^^^^^ E731 | = help: Rewrite `f` as a `def` ℹ Suggested fix -1 1 | #: E731 -2 2 | f = lambda x: 2 * x -3 3 | #: E731 -4 |-f = lambda x: 2 * x - 4 |+def f(x): - 5 |+ return 2 * x -5 6 | #: E731 -6 7 | while False: -7 8 | this = lambda y, z: 2 * x +5 5 | +6 6 | def scope(): +7 7 | # E731 +8 |- f = lambda x: 2 * x + 8 |+ def f(x): + 9 |+ return 2 * x +9 10 | +10 11 | +11 12 | def scope(): -E731.py:7:5: E731 [*] Do not assign a `lambda` expression, use a `def` - | -5 | #: E731 -6 | while False: -7 | this = lambda y, z: 2 * x - | ^^^^^^^^^^^^^^^^^^^^^^^^^ E731 -8 | #: E731 -9 | f = lambda: (yield 1) - | - = help: Rewrite `this` as a `def` +E731.py:14:9: E731 [*] Do not assign a `lambda` expression, use a `def` + | +12 | # E731 +13 | while False: +14 | this = lambda y, z: 2 * x + | ^^^^^^^^^^^^^^^^^^^^^^^^^ E731 + | + = help: Rewrite `this` as a `def` ℹ Suggested fix -4 4 | f = lambda x: 2 * x -5 5 | #: E731 -6 6 | while False: -7 |- this = lambda y, z: 2 * x - 7 |+ def this(y, z): - 8 |+ return 2 * x -8 9 | #: E731 -9 10 | f = lambda: (yield 1) -10 11 | #: E731 +11 11 | def scope(): +12 12 | # E731 +13 13 | while False: +14 |- this = lambda y, z: 2 * x + 14 |+ def this(y, z): + 15 |+ return 2 * x +15 16 | +16 17 | +17 18 | def scope(): -E731.py:9:1: E731 [*] Do not assign a `lambda` expression, use a `def` +E731.py:19:5: E731 [*] Do not assign a `lambda` expression, use a `def` | - 7 | this = lambda y, z: 2 * x - 8 | #: E731 - 9 | f = lambda: (yield 1) - | ^^^^^^^^^^^^^^^^^^^^^ E731 -10 | #: E731 -11 | f = lambda: (yield from g()) +17 | def scope(): +18 | # E731 +19 | f = lambda: (yield 1) + | ^^^^^^^^^^^^^^^^^^^^^ E731 | = help: Rewrite `f` as a `def` ℹ Suggested fix -6 6 | while False: -7 7 | this = lambda y, z: 2 * x -8 8 | #: E731 -9 |-f = lambda: (yield 1) - 9 |+def f(): - 10 |+ return (yield 1) -10 11 | #: E731 -11 12 | f = lambda: (yield from g()) -12 13 | #: E731 +16 16 | +17 17 | def scope(): +18 18 | # E731 +19 |- f = lambda: (yield 1) + 19 |+ def f(): + 20 |+ return (yield 1) +20 21 | +21 22 | +22 23 | def scope(): -E731.py:11:1: E731 [*] Do not assign a `lambda` expression, use a `def` +E731.py:24:5: E731 [*] Do not assign a `lambda` expression, use a `def` | - 9 | f = lambda: (yield 1) -10 | #: E731 -11 | f = lambda: (yield from g()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 -12 | #: E731 -13 | class F: +22 | def scope(): +23 | # E731 +24 | f = lambda: (yield from g()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 | = help: Rewrite `f` as a `def` ℹ Suggested fix -8 8 | #: E731 -9 9 | f = lambda: (yield 1) -10 10 | #: E731 -11 |-f = lambda: (yield from g()) - 11 |+def f(): - 12 |+ return (yield from g()) -12 13 | #: E731 -13 14 | class F: -14 15 | f = lambda x: 2 * x +21 21 | +22 22 | def scope(): +23 23 | # E731 +24 |- f = lambda: (yield from g()) + 24 |+ def f(): + 25 |+ return (yield from g()) +25 26 | +26 27 | +27 28 | def scope(): -E731.py:14:5: E731 Do not assign a `lambda` expression, use a `def` +E731.py:57:5: E731 [*] Do not assign a `lambda` expression, use a `def` | -12 | #: E731 -13 | class F: -14 | f = lambda x: 2 * x +55 | class Scope: +56 | # E731 +57 | f = lambda x: 2 * x | ^^^^^^^^^^^^^^^^^^^ E731 | = help: Rewrite `f` as a `def` -E731.py:32:1: E731 [*] Do not assign a `lambda` expression, use a `def` +ℹ Suggested fix +54 54 | +55 55 | class Scope: +56 56 | # E731 +57 |- f = lambda x: 2 * x + 57 |+ def f(x): + 58 |+ return 2 * x +58 59 | +59 60 | +60 61 | class Scope: + +E731.py:64:5: E731 [*] Do not assign a `lambda` expression, use a `def` | -31 | # ParamSpec cannot be used in this context, so do not preserve the annotation. -32 | f: Callable[P, int] = lambda *args: len(args) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 -33 | f: Callable[[], None] = lambda: None -34 | f: Callable[..., None] = lambda a, b: None +63 | # E731 +64 | f: Callable[[int], int] = lambda x: 2 * x + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 + | + = help: Rewrite `f` as a `def` + +ℹ Possible fix +61 61 | from typing import Callable +62 62 | +63 63 | # E731 +64 |- f: Callable[[int], int] = lambda x: 2 * x + 64 |+ def f(x: int) -> int: + 65 |+ return 2 * x +65 66 | +66 67 | +67 68 | def scope(): + +E731.py:73:9: E731 [*] Do not assign a `lambda` expression, use a `def` + | +71 | x: Callable[[int], int] +72 | if True: +73 | x = lambda: 1 + | ^^^^^^^^^^^^^ E731 +74 | else: +75 | x = lambda: 2 + | + = help: Rewrite `x` as a `def` + +ℹ Possible fix +70 70 | +71 71 | x: Callable[[int], int] +72 72 | if True: +73 |- x = lambda: 1 + 73 |+ def x(): + 74 |+ return 1 +74 75 | else: +75 76 | x = lambda: 2 +76 77 | return x + +E731.py:75:9: E731 [*] Do not assign a `lambda` expression, use a `def` + | +73 | x = lambda: 1 +74 | else: +75 | x = lambda: 2 + | ^^^^^^^^^^^^^ E731 +76 | return x + | + = help: Rewrite `x` as a `def` + +ℹ Possible fix +72 72 | if True: +73 73 | x = lambda: 1 +74 74 | else: +75 |- x = lambda: 2 + 75 |+ def x(): + 76 |+ return 2 +76 77 | return x +77 78 | +78 79 | + +E731.py:86:5: E731 [*] Do not assign a `lambda` expression, use a `def` + | +84 | # ParamSpec cannot be used in this context, so do not preserve the annotation. +85 | P = ParamSpec("P") +86 | f: Callable[P, int] = lambda *args: len(args) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 | = help: Rewrite `f` as a `def` ℹ Suggested fix -29 29 | P = ParamSpec("P") -30 30 | -31 31 | # ParamSpec cannot be used in this context, so do not preserve the annotation. -32 |-f: Callable[P, int] = lambda *args: len(args) - 32 |+def f(*args): - 33 |+ return len(args) -33 34 | f: Callable[[], None] = lambda: None -34 35 | f: Callable[..., None] = lambda a, b: None -35 36 | f: Callable[[int], int] = lambda x: 2 * x +83 83 | +84 84 | # ParamSpec cannot be used in this context, so do not preserve the annotation. +85 85 | P = ParamSpec("P") +86 |- f: Callable[P, int] = lambda *args: len(args) + 86 |+ def f(*args): + 87 |+ return len(args) +87 88 | +88 89 | +89 90 | def scope(): -E731.py:33:1: E731 [*] Do not assign a `lambda` expression, use a `def` +E731.py:94:5: E731 [*] Do not assign a `lambda` expression, use a `def` | -31 | # ParamSpec cannot be used in this context, so do not preserve the annotation. -32 | f: Callable[P, int] = lambda *args: len(args) -33 | f: Callable[[], None] = lambda: None - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 -34 | f: Callable[..., None] = lambda a, b: None -35 | f: Callable[[int], int] = lambda x: 2 * x +92 | from typing import Callable +93 | +94 | f: Callable[[], None] = lambda: None + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 | = help: Rewrite `f` as a `def` ℹ Suggested fix -30 30 | -31 31 | # ParamSpec cannot be used in this context, so do not preserve the annotation. -32 32 | f: Callable[P, int] = lambda *args: len(args) -33 |-f: Callable[[], None] = lambda: None - 33 |+def f() -> None: - 34 |+ return None -34 35 | f: Callable[..., None] = lambda a, b: None -35 36 | f: Callable[[int], int] = lambda x: 2 * x -36 37 | +91 91 | +92 92 | from typing import Callable +93 93 | +94 |- f: Callable[[], None] = lambda: None + 94 |+ def f() -> None: + 95 |+ return None +95 96 | +96 97 | +97 98 | def scope(): -E731.py:34:1: E731 [*] Do not assign a `lambda` expression, use a `def` - | -32 | f: Callable[P, int] = lambda *args: len(args) -33 | f: Callable[[], None] = lambda: None -34 | f: Callable[..., None] = lambda a, b: None - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 -35 | f: Callable[[int], int] = lambda x: 2 * x - | - = help: Rewrite `f` as a `def` +E731.py:102:5: E731 [*] Do not assign a `lambda` expression, use a `def` + | +100 | from typing import Callable +101 | +102 | f: Callable[..., None] = lambda a, b: None + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 + | + = help: Rewrite `f` as a `def` ℹ Suggested fix -31 31 | # ParamSpec cannot be used in this context, so do not preserve the annotation. -32 32 | f: Callable[P, int] = lambda *args: len(args) -33 33 | f: Callable[[], None] = lambda: None -34 |-f: Callable[..., None] = lambda a, b: None - 34 |+def f(a, b) -> None: - 35 |+ return None -35 36 | f: Callable[[int], int] = lambda x: 2 * x -36 37 | -37 38 | # Let's use the `Callable` type from `collections.abc` instead. +99 99 | +100 100 | from typing import Callable +101 101 | +102 |- f: Callable[..., None] = lambda a, b: None + 102 |+ def f(a, b) -> None: + 103 |+ return None +103 104 | +104 105 | +105 106 | def scope(): -E731.py:35:1: E731 [*] Do not assign a `lambda` expression, use a `def` - | -33 | f: Callable[[], None] = lambda: None -34 | f: Callable[..., None] = lambda a, b: None -35 | f: Callable[[int], int] = lambda x: 2 * x - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 -36 | -37 | # Let's use the `Callable` type from `collections.abc` instead. - | - = help: Rewrite `f` as a `def` +E731.py:110:5: E731 [*] Do not assign a `lambda` expression, use a `def` + | +108 | from typing import Callable +109 | +110 | f: Callable[[int], int] = lambda x: 2 * x + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 + | + = help: Rewrite `f` as a `def` ℹ Suggested fix -32 32 | f: Callable[P, int] = lambda *args: len(args) -33 33 | f: Callable[[], None] = lambda: None -34 34 | f: Callable[..., None] = lambda a, b: None -35 |-f: Callable[[int], int] = lambda x: 2 * x - 35 |+def f(x: int) -> int: - 36 |+ return 2 * x -36 37 | -37 38 | # Let's use the `Callable` type from `collections.abc` instead. -38 39 | from collections.abc import Callable +107 107 | +108 108 | from typing import Callable +109 109 | +110 |- f: Callable[[int], int] = lambda x: 2 * x + 110 |+ def f(x: int) -> int: + 111 |+ return 2 * x +111 112 | +112 113 | +113 114 | # Let's use the `Callable` type from `collections.abc` instead. -E731.py:40:1: E731 [*] Do not assign a `lambda` expression, use a `def` - | -38 | from collections.abc import Callable -39 | -40 | f: Callable[[str, int], str] = lambda a, b: a * b - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 -41 | f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b) -42 | f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b] - | - = help: Rewrite `f` as a `def` +E731.py:119:5: E731 [*] Do not assign a `lambda` expression, use a `def` + | +117 | from collections.abc import Callable +118 | +119 | f: Callable[[str, int], str] = lambda a, b: a * b + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 + | + = help: Rewrite `f` as a `def` ℹ Suggested fix -37 37 | # Let's use the `Callable` type from `collections.abc` instead. -38 38 | from collections.abc import Callable -39 39 | -40 |-f: Callable[[str, int], str] = lambda a, b: a * b - 40 |+def f(a: str, b: int) -> str: - 41 |+ return a * b -41 42 | f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b) -42 43 | f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b] -43 44 | +116 116 | +117 117 | from collections.abc import Callable +118 118 | +119 |- f: Callable[[str, int], str] = lambda a, b: a * b + 119 |+ def f(a: str, b: int) -> str: + 120 |+ return a * b +120 121 | +121 122 | +122 123 | def scope(): -E731.py:41:1: E731 [*] Do not assign a `lambda` expression, use a `def` - | -40 | f: Callable[[str, int], str] = lambda a, b: a * b -41 | f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 -42 | f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b] - | - = help: Rewrite `f` as a `def` +E731.py:127:5: E731 [*] Do not assign a `lambda` expression, use a `def` + | +125 | from collections.abc import Callable +126 | +127 | f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 + | + = help: Rewrite `f` as a `def` ℹ Suggested fix -38 38 | from collections.abc import Callable -39 39 | -40 40 | f: Callable[[str, int], str] = lambda a, b: a * b -41 |-f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b) - 41 |+def f(a: str, b: int) -> tuple[str, int]: - 42 |+ return a, b -42 43 | f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b] -43 44 | -44 45 | +124 124 | +125 125 | from collections.abc import Callable +126 126 | +127 |- f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b) + 127 |+ def f(a: str, b: int) -> tuple[str, int]: + 128 |+ return a, b +128 129 | +129 130 | +130 131 | def scope(): -E731.py:42:1: E731 [*] Do not assign a `lambda` expression, use a `def` - | -40 | f: Callable[[str, int], str] = lambda a, b: a * b -41 | f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b) -42 | f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 - | - = help: Rewrite `f` as a `def` +E731.py:135:5: E731 [*] Do not assign a `lambda` expression, use a `def` + | +133 | from collections.abc import Callable +134 | +135 | f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 + | + = help: Rewrite `f` as a `def` ℹ Suggested fix -39 39 | -40 40 | f: Callable[[str, int], str] = lambda a, b: a * b -41 41 | f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b) -42 |-f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b] - 42 |+def f(a: str, b: int, /, c: list[str]) -> list[str]: - 43 |+ return [*c, a * b] -43 44 | -44 45 | -45 46 | # Override `Callable` - -E731.py:51:1: E731 [*] Do not assign a `lambda` expression, use a `def` - | -50 | # Do not copy the annotation from here on out. -51 | f: Callable[[str, int], str] = lambda a, b: a * b - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731 - | - = help: Rewrite `f` as a `def` - -ℹ Suggested fix -48 48 | -49 49 | -50 50 | # Do not copy the annotation from here on out. -51 |-f: Callable[[str, int], str] = lambda a, b: a * b - 51 |+def f(a, b): - 52 |+ return a * b +132 132 | +133 133 | from collections.abc import Callable +134 134 | +135 |- f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b] + 135 |+ def f(a: str, b: int, /, c: list[str]) -> list[str]: + 136 |+ return [*c, a * b]