diff --git a/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py b/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py index 475506225a876..c1cf98bf86786 100644 --- a/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py +++ b/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py @@ -68,6 +68,20 @@ def this_is_also_wrong(value={}): ... +class Foo: + @staticmethod + def this_is_also_wrong_and_more_indented(value={}): + pass + + +def multiline_arg_wrong(value={ + +}): + ... + +def single_line_func_wrong(value = {}): ... + + def and_this(value=set()): ... diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index 54bc6ee478230..2b5fee19ca8b4 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -343,6 +343,7 @@ where returns, args, body, + range, .. }) | Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { @@ -351,8 +352,12 @@ where returns, args, body, + range, .. }) => { + if self.enabled(Rule::MutableArgumentDefault) { + flake8_bugbear::rules::mutable_argument_default(self, args, body, *range); + } if self.enabled(Rule::DjangoNonLeadingReceiverDecorator) { flake8_django::rules::non_leading_receiver_decorator(self, decorator_list); } @@ -4010,9 +4015,6 @@ where } fn visit_arguments(&mut self, arguments: &'b Arguments) { - if self.enabled(Rule::MutableArgumentDefault) { - flake8_bugbear::rules::mutable_argument_default(self, arguments); - } if self.enabled(Rule::FunctionCallInDefaultArgument) { flake8_bugbear::rules::function_call_argument_default(self, arguments); } diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs index 4af5133187d41..4c8054bc36d6a 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs @@ -1,8 +1,15 @@ -use rustpython_parser::ast::{ArgWithDefault, Arguments, Ranged}; +use rustpython_parser::ast::{ArgWithDefault, Arguments, Ranged, Stmt}; -use ruff_diagnostics::{Diagnostic, Violation}; +use crate::registry::AsRule; +use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation}; use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::docstrings::leading_space; +use ruff_python_ast::source_code::Locator; +use ruff_python_ast::whitespace::indentation_at_offset; use ruff_python_semantic::analyze::typing::{is_immutable_annotation, is_mutable_expr}; +use ruff_text_size::TextRange; +use rustpython_parser::lexer::lex_starts_at; +use rustpython_parser::{Mode, Tok}; use crate::checkers::ast::Checker; @@ -50,14 +57,26 @@ use crate::checkers::ast::Checker; pub struct MutableArgumentDefault; impl Violation for MutableArgumentDefault { + const AUTOFIX: AutofixKind = AutofixKind::Sometimes; + #[derive_message_formats] fn message(&self) -> String { - format!("Do not use mutable data structures for argument defaults") + format!("Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`") + } + fn autofix_title(&self) -> Option { + Some(format!( + "Do not use mutable data structures for argument defaults" + )) } } /// B006 -pub(crate) fn mutable_argument_default(checker: &mut Checker, arguments: &Arguments) { +pub(crate) fn mutable_argument_default( + checker: &mut Checker, + arguments: &Arguments, + body: &[Stmt], + func_range: TextRange, +) { // Scan in reverse order to right-align zip(). for ArgWithDefault { def, @@ -78,9 +97,53 @@ pub(crate) fn mutable_argument_default(checker: &mut Checker, arguments: &Argume is_immutable_annotation(expr, checker.semantic()) }) { - checker - .diagnostics - .push(Diagnostic::new(MutableArgumentDefault, default.range())); + let mut diagnostic = Diagnostic::new(MutableArgumentDefault, default.range()); + + // If the function body is on the same line as the function def, do not fix + if checker.patch(diagnostic.kind.rule()) + && !is_single_line(checker.locator, func_range, body) + { + // Set the default arg value to None + let arg_edit = Edit::range_replacement("None".to_string(), default.range()); + + // Add conditional check to set the default arg to its original value if still None + let mut check_lines = String::new(); + let indentation = + indentation_at_offset(checker.locator, body[0].start()).unwrap_or_default(); + let indentation = leading_space(indentation); + // body[0].start() starts at correct indentation so we do need to add indentation + // before pushing the if statement + check_lines.push_str(format!("if {} is None:\n", def.arg.as_str()).as_str()); + check_lines.push_str(indentation); + check_lines.push_str(checker.stylist.indentation()); + check_lines.push_str( + format!( + "{} = {}", + def.arg.as_str(), + checker.generator().expr(default), + ) + .as_str(), + ); + check_lines.push_str(&checker.stylist.line_ending()); + check_lines.push_str(indentation); + let check_edit = Edit::insertion(check_lines, body[0].start()); + + diagnostic.set_fix(Fix::manual_edits(arg_edit, [check_edit])); + } + checker.diagnostics.push(diagnostic); + } + } +} + +fn is_single_line(locator: &Locator, func_range: TextRange, body: &[Stmt]) -> bool { + let arg_string = locator.slice(func_range); + for (tok, range) in lex_starts_at(arg_string, Mode::Module, func_range.start()).flatten() { + match tok { + Tok::Colon => { + return !locator.contains_line_break(TextRange::new(range.end(), body[0].start())) + } + _ => continue, } } + false } diff --git a/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_B008.py.snap b/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_B008.py.snap index f1fc165498682..f92ee0695e17c 100644 --- a/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_B008.py.snap +++ b/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_B008.py.snap @@ -1,113 +1,351 @@ --- source: crates/ruff/src/rules/flake8_bugbear/mod.rs --- -B006_B008.py:63:25: B006 Do not use mutable data structures for argument defaults +B006_B008.py:63:25: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` | 63 | def this_is_wrong(value=[1, 2, 3]): | ^^^^^^^^^ B006 64 | ... | + = help: Do not use mutable data structures for argument defaults -B006_B008.py:67:30: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +60 60 | # Flag mutable literals/comprehensions +61 61 | +62 62 | +63 |-def this_is_wrong(value=[1, 2, 3]): + 63 |+def this_is_wrong(value=None): + 64 |+ if value is None: + 65 |+ value = [1, 2, 3] +64 66 | ... +65 67 | +66 68 | + +B006_B008.py:67:30: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` | 67 | def this_is_also_wrong(value={}): | ^^ B006 68 | ... | + = help: Do not use mutable data structures for argument defaults + +ℹ Possible fix +64 64 | ... +65 65 | +66 66 | +67 |-def this_is_also_wrong(value={}): + 67 |+def this_is_also_wrong(value=None): + 68 |+ if value is None: + 69 |+ value = {} +68 70 | ... +69 71 | +70 72 | -B006_B008.py:71:20: B006 Do not use mutable data structures for argument defaults +B006_B008.py:73:52: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` | -71 | def and_this(value=set()): - | ^^^^^ B006 -72 | ... +71 | class Foo: +72 | @staticmethod +73 | def this_is_also_wrong_and_more_indented(value={}): + | ^^ B006 +74 | pass | + = help: Do not use mutable data structures for argument defaults -B006_B008.py:75:20: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +70 70 | +71 71 | class Foo: +72 72 | @staticmethod +73 |- def this_is_also_wrong_and_more_indented(value={}): + 73 |+ def this_is_also_wrong_and_more_indented(value=None): + 74 |+ if value is None: + 75 |+ value = {} +74 76 | pass +75 77 | +76 78 | + +B006_B008.py:77:31: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` | -75 | def this_too(value=collections.OrderedDict()): - | ^^^^^^^^^^^^^^^^^^^^^^^^^ B006 -76 | ... +77 | def multiline_arg_wrong(value={ + | _______________________________^ +78 | | +79 | | }): + | |_^ B006 +80 | ... | + = help: Do not use mutable data structures for argument defaults -B006_B008.py:79:32: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +74 74 | pass +75 75 | +76 76 | +77 |-def multiline_arg_wrong(value={ +78 |- +79 |-}): + 77 |+def multiline_arg_wrong(value=None): + 78 |+ if value is None: + 79 |+ value = {} +80 80 | ... +81 81 | +82 82 | def single_line_func_wrong(value = {}): ... + +B006_B008.py:82:36: B006 Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` | -79 | async def async_this_too(value=collections.defaultdict()): - | ^^^^^^^^^^^^^^^^^^^^^^^^^ B006 80 | ... +81 | +82 | def single_line_func_wrong(value = {}): ... + | ^^ B006 | + = help: Do not use mutable data structures for argument defaults -B006_B008.py:83:26: B006 Do not use mutable data structures for argument defaults +B006_B008.py:85:20: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` | -83 | def dont_forget_me(value=collections.deque()): - | ^^^^^^^^^^^^^^^^^^^ B006 -84 | ... +85 | def and_this(value=set()): + | ^^^^^ B006 +86 | ... | + = help: Do not use mutable data structures for argument defaults -B006_B008.py:88:46: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +82 82 | def single_line_func_wrong(value = {}): ... +83 83 | +84 84 | +85 |-def and_this(value=set()): + 85 |+def and_this(value=None): + 86 |+ if value is None: + 87 |+ value = set() +86 88 | ... +87 89 | +88 90 | + +B006_B008.py:89:20: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` | -87 | # N.B. we're also flagging the function call in the comprehension -88 | def list_comprehension_also_not_okay(default=[i**2 for i in range(3)]): - | ^^^^^^^^^^^^^^^^^^^^^^^^ B006 -89 | pass +89 | def this_too(value=collections.OrderedDict()): + | ^^^^^^^^^^^^^^^^^^^^^^^^^ B006 +90 | ... | + = help: Do not use mutable data structures for argument defaults -B006_B008.py:92:46: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +86 86 | ... +87 87 | +88 88 | +89 |-def this_too(value=collections.OrderedDict()): + 89 |+def this_too(value=None): + 90 |+ if value is None: + 91 |+ value = collections.OrderedDict() +90 92 | ... +91 93 | +92 94 | + +B006_B008.py:93:32: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` | -92 | def dict_comprehension_also_not_okay(default={i: i**2 for i in range(3)}): - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ B006 -93 | pass +93 | async def async_this_too(value=collections.defaultdict()): + | ^^^^^^^^^^^^^^^^^^^^^^^^^ B006 +94 | ... | + = help: Do not use mutable data structures for argument defaults -B006_B008.py:96:45: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +90 90 | ... +91 91 | +92 92 | +93 |-async def async_this_too(value=collections.defaultdict()): + 93 |+async def async_this_too(value=None): + 94 |+ if value is None: + 95 |+ value = collections.defaultdict() +94 96 | ... +95 97 | +96 98 | + +B006_B008.py:97:26: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` | -96 | def set_comprehension_also_not_okay(default={i**2 for i in range(3)}): - | ^^^^^^^^^^^^^^^^^^^^^^^^ B006 -97 | pass +97 | def dont_forget_me(value=collections.deque()): + | ^^^^^^^^^^^^^^^^^^^ B006 +98 | ... | + = help: Do not use mutable data structures for argument defaults -B006_B008.py:100:33: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +94 94 | ... +95 95 | +96 96 | +97 |-def dont_forget_me(value=collections.deque()): + 97 |+def dont_forget_me(value=None): + 98 |+ if value is None: + 99 |+ value = collections.deque() +98 100 | ... +99 101 | +100 102 | + +B006_B008.py:102:46: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` + | +101 | # N.B. we're also flagging the function call in the comprehension +102 | def list_comprehension_also_not_okay(default=[i**2 for i in range(3)]): + | ^^^^^^^^^^^^^^^^^^^^^^^^ B006 +103 | pass | -100 | def kwonlyargs_mutable(*, value=[]): + = help: Do not use mutable data structures for argument defaults + +ℹ Possible fix +99 99 | +100 100 | +101 101 | # N.B. we're also flagging the function call in the comprehension +102 |-def list_comprehension_also_not_okay(default=[i**2 for i in range(3)]): + 102 |+def list_comprehension_also_not_okay(default=None): + 103 |+ if default is None: + 104 |+ default = [(i ** 2) for i in range(3)] +103 105 | pass +104 106 | +105 107 | + +B006_B008.py:106:46: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` + | +106 | def dict_comprehension_also_not_okay(default={i: i**2 for i in range(3)}): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ B006 +107 | pass + | + = help: Do not use mutable data structures for argument defaults + +ℹ Possible fix +103 103 | pass +104 104 | +105 105 | +106 |-def dict_comprehension_also_not_okay(default={i: i**2 for i in range(3)}): + 106 |+def dict_comprehension_also_not_okay(default=None): + 107 |+ if default is None: + 108 |+ default = {i: (i ** 2) for i in range(3)} +107 109 | pass +108 110 | +109 111 | + +B006_B008.py:110:45: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` + | +110 | def set_comprehension_also_not_okay(default={i**2 for i in range(3)}): + | ^^^^^^^^^^^^^^^^^^^^^^^^ B006 +111 | pass + | + = help: Do not use mutable data structures for argument defaults + +ℹ Possible fix +107 107 | pass +108 108 | +109 109 | +110 |-def set_comprehension_also_not_okay(default={i**2 for i in range(3)}): + 110 |+def set_comprehension_also_not_okay(default=None): + 111 |+ if default is None: + 112 |+ default = {(i ** 2) for i in range(3)} +111 113 | pass +112 114 | +113 115 | + +B006_B008.py:114:33: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` + | +114 | def kwonlyargs_mutable(*, value=[]): | ^^ B006 -101 | ... +115 | ... | + = help: Do not use mutable data structures for argument defaults + +ℹ Possible fix +111 111 | pass +112 112 | +113 113 | +114 |-def kwonlyargs_mutable(*, value=[]): + 114 |+def kwonlyargs_mutable(*, value=None): + 115 |+ if value is None: + 116 |+ value = [] +115 117 | ... +116 118 | +117 119 | -B006_B008.py:221:20: B006 Do not use mutable data structures for argument defaults +B006_B008.py:235:20: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` | -219 | # B006 and B008 -220 | # We should handle arbitrary nesting of these B008. -221 | def nested_combo(a=[float(3), dt.datetime.now()]): +233 | # B006 and B008 +234 | # We should handle arbitrary nesting of these B008. +235 | def nested_combo(a=[float(3), dt.datetime.now()]): | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B006 -222 | pass +236 | pass | + = help: Do not use mutable data structures for argument defaults -B006_B008.py:254:27: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +232 232 | +233 233 | # B006 and B008 +234 234 | # We should handle arbitrary nesting of these B008. +235 |-def nested_combo(a=[float(3), dt.datetime.now()]): + 235 |+def nested_combo(a=None): + 236 |+ if a is None: + 237 |+ a = [float(3), dt.datetime.now()] +236 238 | pass +237 239 | +238 240 | + +B006_B008.py:268:27: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` | -253 | def mutable_annotations( -254 | a: list[int] | None = [], +267 | def mutable_annotations( +268 | a: list[int] | None = [], | ^^ B006 -255 | b: Optional[Dict[int, int]] = {}, -256 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +269 | b: Optional[Dict[int, int]] = {}, +270 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), | + = help: Do not use mutable data structures for argument defaults + +ℹ Possible fix +265 265 | +266 266 | +267 267 | def mutable_annotations( +268 |- a: list[int] | None = [], + 268 |+ a: list[int] | None = None, +269 269 | b: Optional[Dict[int, int]] = {}, +270 270 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +271 271 | ): + 272 |+ if a is None: + 273 |+ a = [] +272 274 | pass -B006_B008.py:255:35: B006 Do not use mutable data structures for argument defaults +B006_B008.py:269:35: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` | -253 | def mutable_annotations( -254 | a: list[int] | None = [], -255 | b: Optional[Dict[int, int]] = {}, +267 | def mutable_annotations( +268 | a: list[int] | None = [], +269 | b: Optional[Dict[int, int]] = {}, | ^^ B006 -256 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), -257 | ): +270 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +271 | ): | + = help: Do not use mutable data structures for argument defaults -B006_B008.py:256:62: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +266 266 | +267 267 | def mutable_annotations( +268 268 | a: list[int] | None = [], +269 |- b: Optional[Dict[int, int]] = {}, + 269 |+ b: Optional[Dict[int, int]] = None, +270 270 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +271 271 | ): + 272 |+ if b is None: + 273 |+ b = {} +272 274 | pass + +B006_B008.py:270:62: B006 [*] Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` | -254 | a: list[int] | None = [], -255 | b: Optional[Dict[int, int]] = {}, -256 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +268 | a: list[int] | None = [], +269 | b: Optional[Dict[int, int]] = {}, +270 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), | ^^^^^ B006 -257 | ): -258 | pass +271 | ): +272 | pass | + = help: Do not use mutable data structures for argument defaults + +ℹ Possible fix +267 267 | def mutable_annotations( +268 268 | a: list[int] | None = [], +269 269 | b: Optional[Dict[int, int]] = {}, +270 |- c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), + 270 |+ c: Annotated[Union[Set[str], abc.Sized], "annotation"] = None, +271 271 | ): + 272 |+ if c is None: + 273 |+ c = set() +272 274 | pass diff --git a/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B008_B006_B008.py.snap b/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B008_B006_B008.py.snap index 36a464813a00c..dc747bc5b4a8e 100644 --- a/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B008_B006_B008.py.snap +++ b/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B008_B006_B008.py.snap @@ -1,83 +1,83 @@ --- source: crates/ruff/src/rules/flake8_bugbear/mod.rs --- -B006_B008.py:88:61: B008 Do not perform function call `range` in argument defaults - | -87 | # N.B. we're also flagging the function call in the comprehension -88 | def list_comprehension_also_not_okay(default=[i**2 for i in range(3)]): - | ^^^^^^^^ B008 -89 | pass - | +B006_B008.py:102:61: B008 Do not perform function call `range` in argument defaults + | +101 | # N.B. we're also flagging the function call in the comprehension +102 | def list_comprehension_also_not_okay(default=[i**2 for i in range(3)]): + | ^^^^^^^^ B008 +103 | pass + | -B006_B008.py:92:64: B008 Do not perform function call `range` in argument defaults - | -92 | def dict_comprehension_also_not_okay(default={i: i**2 for i in range(3)}): - | ^^^^^^^^ B008 -93 | pass - | +B006_B008.py:106:64: B008 Do not perform function call `range` in argument defaults + | +106 | def dict_comprehension_also_not_okay(default={i: i**2 for i in range(3)}): + | ^^^^^^^^ B008 +107 | pass + | -B006_B008.py:96:60: B008 Do not perform function call `range` in argument defaults - | -96 | def set_comprehension_also_not_okay(default={i**2 for i in range(3)}): - | ^^^^^^^^ B008 -97 | pass - | +B006_B008.py:110:60: B008 Do not perform function call `range` in argument defaults + | +110 | def set_comprehension_also_not_okay(default={i**2 for i in range(3)}): + | ^^^^^^^^ B008 +111 | pass + | -B006_B008.py:112:39: B008 Do not perform function call `time.time` in argument defaults +B006_B008.py:126:39: B008 Do not perform function call `time.time` in argument defaults | -110 | # B008 -111 | # Flag function calls as default args (including if they are part of a sub-expression) -112 | def in_fact_all_calls_are_wrong(value=time.time()): +124 | # B008 +125 | # Flag function calls as default args (including if they are part of a sub-expression) +126 | def in_fact_all_calls_are_wrong(value=time.time()): | ^^^^^^^^^^^ B008 -113 | ... +127 | ... | -B006_B008.py:116:12: B008 Do not perform function call `dt.datetime.now` in argument defaults +B006_B008.py:130:12: B008 Do not perform function call `dt.datetime.now` in argument defaults | -116 | def f(when=dt.datetime.now() + dt.timedelta(days=7)): +130 | def f(when=dt.datetime.now() + dt.timedelta(days=7)): | ^^^^^^^^^^^^^^^^^ B008 -117 | pass +131 | pass | -B006_B008.py:120:30: B008 Do not perform function call in argument defaults +B006_B008.py:134:30: B008 Do not perform function call in argument defaults | -120 | def can_even_catch_lambdas(a=(lambda x: x)()): +134 | def can_even_catch_lambdas(a=(lambda x: x)()): | ^^^^^^^^^^^^^^^ B008 -121 | ... +135 | ... | -B006_B008.py:221:31: B008 Do not perform function call `dt.datetime.now` in argument defaults +B006_B008.py:235:31: B008 Do not perform function call `dt.datetime.now` in argument defaults | -219 | # B006 and B008 -220 | # We should handle arbitrary nesting of these B008. -221 | def nested_combo(a=[float(3), dt.datetime.now()]): +233 | # B006 and B008 +234 | # We should handle arbitrary nesting of these B008. +235 | def nested_combo(a=[float(3), dt.datetime.now()]): | ^^^^^^^^^^^^^^^^^ B008 -222 | pass +236 | pass | -B006_B008.py:227:22: B008 Do not perform function call `map` in argument defaults +B006_B008.py:241:22: B008 Do not perform function call `map` in argument defaults | -225 | # Don't flag nested B006 since we can't guarantee that -226 | # it isn't made mutable by the outer operation. -227 | def no_nested_b006(a=map(lambda s: s.upper(), ["a", "b", "c"])): +239 | # Don't flag nested B006 since we can't guarantee that +240 | # it isn't made mutable by the outer operation. +241 | def no_nested_b006(a=map(lambda s: s.upper(), ["a", "b", "c"])): | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B008 -228 | pass +242 | pass | -B006_B008.py:232:19: B008 Do not perform function call `random.randint` in argument defaults +B006_B008.py:246:19: B008 Do not perform function call `random.randint` in argument defaults | -231 | # B008-ception. -232 | def nested_b008(a=random.randint(0, dt.datetime.now().year)): +245 | # B008-ception. +246 | def nested_b008(a=random.randint(0, dt.datetime.now().year)): | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B008 -233 | pass +247 | pass | -B006_B008.py:232:37: B008 Do not perform function call `dt.datetime.now` in argument defaults +B006_B008.py:246:37: B008 Do not perform function call `dt.datetime.now` in argument defaults | -231 | # B008-ception. -232 | def nested_b008(a=random.randint(0, dt.datetime.now().year)): +245 | # B008-ception. +246 | def nested_b008(a=random.randint(0, dt.datetime.now().year)): | ^^^^^^^^^^^^^^^^^ B008 -233 | pass +247 | pass |