From fbbc982c299168111a4f6c44d152d3e954810038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Wed, 27 Sep 2023 03:26:29 +0200 Subject: [PATCH] Ensure that B006 autofixes are inserted after imports (#7629) ## Summary Fixes #7616 by ensuring that [B006](https://docs.astral.sh/ruff/rules/mutable-argument-default/#mutable-argument-default-b006) fixes are inserted after module imports. I have created a new test file, `B006_5.py`. This is mainly because I have been working on this on and off, and the merge conflicts were easier to handle in a separate file. If needed, I can move it into another file. ## Test Plan `cargo test` --- .../test/fixtures/flake8_bugbear/B006_5.py | 74 +++++ .../test/fixtures/flake8_bugbear/B006_6.py | 5 + .../test/fixtures/flake8_bugbear/B006_7.py | 5 + .../src/rules/flake8_bugbear/mod.rs | 3 + .../rules/mutable_argument_default.rs | 69 ++-- ...flake8_bugbear__tests__B006_B006_5.py.snap | 313 ++++++++++++++++++ ...flake8_bugbear__tests__B006_B006_6.py.snap | 25 ++ ...flake8_bugbear__tests__B006_B006_7.py.snap | 25 ++ ...ke8_bugbear__tests__B006_B006_B008.py.snap | 28 +- 9 files changed, 514 insertions(+), 33 deletions(-) create mode 100644 crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py create mode 100644 crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_6.py create mode 100644 crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_7.py create mode 100644 crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap create mode 100644 crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_6.py.snap create mode 100644 crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_7.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py new file mode 100644 index 0000000000000..b2bde5afa118d --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py @@ -0,0 +1,74 @@ +# Move mutable arguments below imports and docstrings +# https://github.com/astral-sh/ruff/issues/7616 + + +def import_module_wrong(value: dict[str, str] = {}): + import os + + +def import_module_with_values_wrong(value: dict[str, str] = {}): + import os + + return 2 + + +def import_modules_wrong(value: dict[str, str] = {}): + import os + import sys + import itertools + + +def from_import_module_wrong(value: dict[str, str] = {}): + from os import path + + +def from_imports_module_wrong(value: dict[str, str] = {}): + from os import path + from sys import version_info + + +def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + import os + from sys import version_info + + +def import_docstring_module_wrong(value: dict[str, str] = {}): + """Docstring""" + import os + + +def import_module_wrong(value: dict[str, str] = {}): + """Docstring""" + import os; import sys + + +def import_module_wrong(value: dict[str, str] = {}): + """Docstring""" + import os; import sys; x = 1 + + +def import_module_wrong(value: dict[str, str] = {}): + """Docstring""" + import os; import sys + + +def import_module_wrong(value: dict[str, str] = {}): + import os; import sys + + +def import_module_wrong(value: dict[str, str] = {}): + import os; import sys; x = 1 + + +def import_module_wrong(value: dict[str, str] = {}): + import os; import sys + + +def import_module_wrong(value: dict[str, str] = {}): import os + + +def import_module_wrong(value: dict[str, str] = {}): import os; import sys + + +def import_module_wrong(value: dict[str, str] = {}): \ + import os diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_6.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_6.py new file mode 100644 index 0000000000000..b651380f0def1 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_6.py @@ -0,0 +1,5 @@ +# Import followed by whitespace with no newline +# Same as B006_2.py, but import instead of docstring + +def foobar(foor, bar={}): + import os \ No newline at end of file diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_7.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_7.py new file mode 100644 index 0000000000000..0b382b5fbfcf9 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_7.py @@ -0,0 +1,5 @@ +# Import with no newline +# Same as B006_3.py, but import instead of docstring + +def foobar(foor, bar={}): + import os \ No newline at end of file diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs b/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs index 8ce32f9e071fc..e31681ca43a5b 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs @@ -37,6 +37,9 @@ mod tests { #[test_case(Rule::MutableArgumentDefault, Path::new("B006_2.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_3.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_4.py"))] + #[test_case(Rule::MutableArgumentDefault, Path::new("B006_5.py"))] + #[test_case(Rule::MutableArgumentDefault, Path::new("B006_6.py"))] + #[test_case(Rule::MutableArgumentDefault, Path::new("B006_7.py"))] #[test_case(Rule::NoExplicitStacklevel, Path::new("B028.py"))] #[test_case(Rule::RaiseLiteral, Path::new("B016.py"))] #[test_case(Rule::RaiseWithoutFromInsideExcept, Path::new("B904.py"))] diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs index f604d18d0c887..3d664a4da0727 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs @@ -139,16 +139,14 @@ fn move_initialization( indexer: &Indexer, generator: Generator, ) -> Option { - let mut body = function_def.body.iter(); + let mut body = function_def.body.iter().peekable(); - let statement = body.next()?; - if indexer.in_multi_statement_line(statement, locator) { + // Avoid attempting to fix single-line functions. + let statement = body.peek()?; + if indexer.preceded_by_multi_statement_line(statement, locator) { return None; } - // Determine the indentation depth of the function body. - let indentation = indentation_at_offset(statement.start(), locator)?; - // Set the default argument value to `None`. let default_edit = Edit::range_replacement("None".to_string(), default.range()); @@ -164,34 +162,43 @@ fn move_initialization( )); content.push_str(stylist.line_ending().as_str()); + // Determine the indentation depth of the function body. + let indentation = indentation_at_offset(statement.start(), locator)?; + // Indent the edit to match the body indentation. - let content = textwrap::indent(&content, indentation).to_string(); - - let initialization_edit = if is_docstring_stmt(statement) { - // If the first statement in the function is a docstring, insert _after_ it. - if let Some(statement) = body.next() { - // If there's a second statement, insert _before_ it, but ensure this isn't a - // multi-statement line. - if indexer.in_multi_statement_line(statement, locator) { - return None; + let mut content = textwrap::indent(&content, indentation).to_string(); + + // Find the position to insert the initialization after docstring and imports + let mut pos = locator.line_start(statement.start()); + while let Some(statement) = body.next() { + // If the statement is a docstring or an import, insert _after_ it. + if is_docstring_stmt(statement) + || statement.is_import_stmt() + || statement.is_import_from_stmt() + { + if let Some(next) = body.peek() { + // If there's a second statement, insert _before_ it, but ensure this isn't a + // multi-statement line. + if indexer.in_multi_statement_line(statement, locator) { + continue; + } + pos = locator.line_start(next.start()); + } else if locator.full_line_end(statement.end()) == locator.text_len() { + // If the statement is at the end of the file, without a trailing newline, insert + // _after_ it with an extra newline. + content = format!("{}{}", stylist.line_ending().as_str(), content); + pos = locator.full_line_end(statement.end()); + break; + } else { + // If this is the only statement, insert _after_ it. + pos = locator.full_line_end(statement.end()); + break; } - Edit::insertion(content, locator.line_start(statement.start())) - } else if locator.full_line_end(statement.end()) == locator.text_len() { - // If the statement is at the end of the file, without a trailing newline, insert - // _after_ it with an extra newline. - Edit::insertion( - format!("{}{}", stylist.line_ending().as_str(), content), - locator.full_line_end(statement.end()), - ) } else { - // If the docstring is the only statement, insert _after_ it. - Edit::insertion(content, locator.full_line_end(statement.end())) - } - } else { - // Otherwise, insert before the first statement. - let at = locator.line_start(statement.start()); - Edit::insertion(content, at) - }; + break; + }; + } + let initialization_edit = Edit::insertion(content, pos); Some(Fix::manual_edits(default_edit, [initialization_edit])) } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap new file mode 100644 index 0000000000000..d741dc3258eba --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap @@ -0,0 +1,313 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs +--- +B006_5.py:5:49: B006 [*] Do not use mutable data structures for argument defaults + | +5 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +6 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +2 2 | # https://github.com/astral-sh/ruff/issues/7616 +3 3 | +4 4 | +5 |-def import_module_wrong(value: dict[str, str] = {}): + 5 |+def import_module_wrong(value: dict[str, str] = None): +6 6 | import os + 7 |+ if value is None: + 8 |+ value = {} +7 9 | +8 10 | +9 11 | def import_module_with_values_wrong(value: dict[str, str] = {}): + +B006_5.py:9:61: B006 [*] Do not use mutable data structures for argument defaults + | + 9 | def import_module_with_values_wrong(value: dict[str, str] = {}): + | ^^ B006 +10 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +6 6 | import os +7 7 | +8 8 | +9 |-def import_module_with_values_wrong(value: dict[str, str] = {}): + 9 |+def import_module_with_values_wrong(value: dict[str, str] = None): +10 10 | import os +11 11 | + 12 |+ if value is None: + 13 |+ value = {} +12 14 | return 2 +13 15 | +14 16 | + +B006_5.py:15:50: B006 [*] Do not use mutable data structures for argument defaults + | +15 | def import_modules_wrong(value: dict[str, str] = {}): + | ^^ B006 +16 | import os +17 | import sys + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +12 12 | return 2 +13 13 | +14 14 | +15 |-def import_modules_wrong(value: dict[str, str] = {}): + 15 |+def import_modules_wrong(value: dict[str, str] = None): +16 16 | import os +17 17 | import sys +18 18 | import itertools + 19 |+ if value is None: + 20 |+ value = {} +19 21 | +20 22 | +21 23 | def from_import_module_wrong(value: dict[str, str] = {}): + +B006_5.py:21:54: B006 [*] Do not use mutable data structures for argument defaults + | +21 | def from_import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +22 | from os import path + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +18 18 | import itertools +19 19 | +20 20 | +21 |-def from_import_module_wrong(value: dict[str, str] = {}): + 21 |+def from_import_module_wrong(value: dict[str, str] = None): +22 22 | from os import path + 23 |+ if value is None: + 24 |+ value = {} +23 25 | +24 26 | +25 27 | def from_imports_module_wrong(value: dict[str, str] = {}): + +B006_5.py:25:55: B006 [*] Do not use mutable data structures for argument defaults + | +25 | def from_imports_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +26 | from os import path +27 | from sys import version_info + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +22 22 | from os import path +23 23 | +24 24 | +25 |-def from_imports_module_wrong(value: dict[str, str] = {}): + 25 |+def from_imports_module_wrong(value: dict[str, str] = None): +26 26 | from os import path +27 27 | from sys import version_info + 28 |+ if value is None: + 29 |+ value = {} +28 30 | +29 31 | +30 32 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + +B006_5.py:30:66: B006 [*] Do not use mutable data structures for argument defaults + | +30 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +31 | import os +32 | from sys import version_info + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +27 27 | from sys import version_info +28 28 | +29 29 | +30 |-def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + 30 |+def import_and_from_imports_module_wrong(value: dict[str, str] = None): +31 31 | import os +32 32 | from sys import version_info + 33 |+ if value is None: + 34 |+ value = {} +33 35 | +34 36 | +35 37 | def import_docstring_module_wrong(value: dict[str, str] = {}): + +B006_5.py:35:59: B006 [*] Do not use mutable data structures for argument defaults + | +35 | def import_docstring_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +36 | """Docstring""" +37 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +32 32 | from sys import version_info +33 33 | +34 34 | +35 |-def import_docstring_module_wrong(value: dict[str, str] = {}): + 35 |+def import_docstring_module_wrong(value: dict[str, str] = None): +36 36 | """Docstring""" +37 37 | import os + 38 |+ if value is None: + 39 |+ value = {} +38 40 | +39 41 | +40 42 | def import_module_wrong(value: dict[str, str] = {}): + +B006_5.py:40:49: B006 [*] Do not use mutable data structures for argument defaults + | +40 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +41 | """Docstring""" +42 | import os; import sys + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +37 37 | import os +38 38 | +39 39 | +40 |-def import_module_wrong(value: dict[str, str] = {}): + 40 |+def import_module_wrong(value: dict[str, str] = None): +41 41 | """Docstring""" +42 42 | import os; import sys + 43 |+ if value is None: + 44 |+ value = {} +43 45 | +44 46 | +45 47 | def import_module_wrong(value: dict[str, str] = {}): + +B006_5.py:45:49: B006 [*] Do not use mutable data structures for argument defaults + | +45 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +46 | """Docstring""" +47 | import os; import sys; x = 1 + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +42 42 | import os; import sys +43 43 | +44 44 | +45 |-def import_module_wrong(value: dict[str, str] = {}): + 45 |+def import_module_wrong(value: dict[str, str] = None): +46 46 | """Docstring""" + 47 |+ if value is None: + 48 |+ value = {} +47 49 | import os; import sys; x = 1 +48 50 | +49 51 | + +B006_5.py:50:49: B006 [*] Do not use mutable data structures for argument defaults + | +50 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +51 | """Docstring""" +52 | import os; import sys + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +47 47 | import os; import sys; x = 1 +48 48 | +49 49 | +50 |-def import_module_wrong(value: dict[str, str] = {}): + 50 |+def import_module_wrong(value: dict[str, str] = None): +51 51 | """Docstring""" +52 52 | import os; import sys + 53 |+ if value is None: + 54 |+ value = {} +53 55 | +54 56 | +55 57 | def import_module_wrong(value: dict[str, str] = {}): + +B006_5.py:55:49: B006 [*] Do not use mutable data structures for argument defaults + | +55 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +56 | import os; import sys + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +52 52 | import os; import sys +53 53 | +54 54 | +55 |-def import_module_wrong(value: dict[str, str] = {}): + 55 |+def import_module_wrong(value: dict[str, str] = None): +56 56 | import os; import sys + 57 |+ if value is None: + 58 |+ value = {} +57 59 | +58 60 | +59 61 | def import_module_wrong(value: dict[str, str] = {}): + +B006_5.py:59:49: B006 [*] Do not use mutable data structures for argument defaults + | +59 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +60 | import os; import sys; x = 1 + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +56 56 | import os; import sys +57 57 | +58 58 | +59 |-def import_module_wrong(value: dict[str, str] = {}): + 59 |+def import_module_wrong(value: dict[str, str] = None): + 60 |+ if value is None: + 61 |+ value = {} +60 62 | import os; import sys; x = 1 +61 63 | +62 64 | + +B006_5.py:63:49: B006 [*] Do not use mutable data structures for argument defaults + | +63 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +64 | import os; import sys + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +60 60 | import os; import sys; x = 1 +61 61 | +62 62 | +63 |-def import_module_wrong(value: dict[str, str] = {}): + 63 |+def import_module_wrong(value: dict[str, str] = None): +64 64 | import os; import sys + 65 |+ if value is None: + 66 |+ value = {} +65 67 | +66 68 | +67 69 | def import_module_wrong(value: dict[str, str] = {}): import os + +B006_5.py:67:49: B006 Do not use mutable data structures for argument defaults + | +67 | def import_module_wrong(value: dict[str, str] = {}): import os + | ^^ B006 + | + = help: Replace with `None`; initialize within function + +B006_5.py:70:49: B006 Do not use mutable data structures for argument defaults + | +70 | def import_module_wrong(value: dict[str, str] = {}): import os; import sys + | ^^ B006 + | + = help: Replace with `None`; initialize within function + +B006_5.py:73:49: B006 Do not use mutable data structures for argument defaults + | +73 | def import_module_wrong(value: dict[str, str] = {}): \ + | ^^ B006 +74 | import os + | + = help: Replace with `None`; initialize within function + + diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_6.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_6.py.snap new file mode 100644 index 0000000000000..f1af067d82f64 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_6.py.snap @@ -0,0 +1,25 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs +--- +B006_6.py:4:22: B006 [*] Do not use mutable data structures for argument defaults + | +2 | # Same as B006_2.py, but import instead of docstring +3 | +4 | def foobar(foor, bar={}): + | ^^ B006 +5 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +1 1 | # Import followed by whitespace with no newline +2 2 | # Same as B006_2.py, but import instead of docstring +3 3 | +4 |-def foobar(foor, bar={}): +5 |- import os + 4 |+def foobar(foor, bar=None): + 5 |+ import os + 6 |+ if bar is None: + 7 |+ bar = {} + + diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_7.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_7.py.snap new file mode 100644 index 0000000000000..fbe42a7285e85 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_7.py.snap @@ -0,0 +1,25 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs +--- +B006_7.py:4:22: B006 [*] Do not use mutable data structures for argument defaults + | +2 | # Same as B006_3.py, but import instead of docstring +3 | +4 | def foobar(foor, bar={}): + | ^^ B006 +5 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +1 1 | # Import with no newline +2 2 | # Same as B006_3.py, but import instead of docstring +3 3 | +4 |-def foobar(foor, bar={}): +5 |- import os + 4 |+def foobar(foor, bar=None): + 5 |+ import os + 6 |+ if bar is None: + 7 |+ bar = {} + + diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_B008.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_B008.py.snap index 8272eb6aa6a09..6f868ac491c64 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_B008.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_B008.py.snap @@ -424,7 +424,7 @@ B006_B008.py:288:52: B006 [*] Do not use mutable data structures for argument de 291 293 | 292 294 | -B006_B008.py:293:52: B006 Do not use mutable data structures for argument defaults +B006_B008.py:293:52: B006 [*] Do not use mutable data structures for argument defaults | 293 | def single_line_func_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -432,7 +432,19 @@ B006_B008.py:293:52: B006 Do not use mutable data structures for argument defaul | = help: Replace with `None`; initialize within function -B006_B008.py:297:52: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +290 290 | ... +291 291 | +292 292 | +293 |-def single_line_func_wrong(value: dict[str, str] = {}): + 293 |+def single_line_func_wrong(value: dict[str, str] = None): + 294 |+ if value is None: + 295 |+ value = {} +294 296 | """Docstring"""; ... +295 297 | +296 298 | + +B006_B008.py:297:52: B006 [*] Do not use mutable data structures for argument defaults | 297 | def single_line_func_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -441,6 +453,18 @@ B006_B008.py:297:52: B006 Do not use mutable data structures for argument defaul | = help: Replace with `None`; initialize within function +ℹ Possible fix +294 294 | """Docstring"""; ... +295 295 | +296 296 | +297 |-def single_line_func_wrong(value: dict[str, str] = {}): + 297 |+def single_line_func_wrong(value: dict[str, str] = None): + 298 |+ if value is None: + 299 |+ value = {} +298 300 | """Docstring"""; \ +299 301 | ... +300 302 | + B006_B008.py:302:52: B006 [*] Do not use mutable data structures for argument defaults | 302 | def single_line_func_wrong(value: dict[str, str] = {