mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:24 +00:00
[flake8-bugbear
] Add autofix for B006 (#6131)
## Summary Reopening of https://github.com/astral-sh/ruff/pull/4880 One open TODO as described in: https://github.com/astral-sh/ruff/pull/4880#discussion_r1265110215 FYI @charliermarsh seeing as you commented you wanted to do final review and merge. @konstin @dhruvmanila @MichaReiser as previous reviewers. # Old Description ## Summary Adds an autofix for B006 turning mutable argument defaults into None and setting their original value back in the function body if still `None` at runtime like so: ```python def before(x=[]): pass def after(x=None): if x is None: x = [] pass ``` ## Test Plan Added an extra test case to existing fixture with more indentation. Checked results for all old examples. NOTE: Also adapted the jupyter notebook test as this checked for B006 as well. ## Issue link Closes: https://github.com/charliermarsh/ruff/issues/4693 --------- Co-authored-by: konstin <konstin@mailbox.org>
This commit is contained in:
parent
4811af0f0b
commit
50dab9cea6
6 changed files with 461 additions and 132 deletions
|
@ -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()):
|
||||
...
|
||||
|
||||
|
|
|
@ -6,9 +6,6 @@ use crate::rules::{flake8_bugbear, flake8_pyi, ruff};
|
|||
|
||||
/// Run lint rules over a [`Parameters`] syntax node.
|
||||
pub(crate) fn parameters(parameters: &Parameters, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::MutableArgumentDefault) {
|
||||
flake8_bugbear::rules::mutable_argument_default(checker, parameters);
|
||||
}
|
||||
if checker.enabled(Rule::FunctionCallInDefaultArgument) {
|
||||
flake8_bugbear::rules::function_call_in_argument_default(checker, parameters);
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
|||
parameters,
|
||||
body,
|
||||
type_params,
|
||||
range: _,
|
||||
range,
|
||||
}) => {
|
||||
if checker.enabled(Rule::DjangoNonLeadingReceiverDecorator) {
|
||||
flake8_django::rules::non_leading_receiver_decorator(checker, decorator_list);
|
||||
|
@ -204,6 +204,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
|||
if checker.enabled(Rule::CachedInstanceMethod) {
|
||||
flake8_bugbear::rules::cached_instance_method(checker, decorator_list);
|
||||
}
|
||||
if checker.enabled(Rule::MutableArgumentDefault) {
|
||||
flake8_bugbear::rules::mutable_argument_default(checker, parameters, body, *range);
|
||||
}
|
||||
if checker.any_enabled(&[
|
||||
Rule::UnnecessaryReturnNone,
|
||||
Rule::ImplicitReturnValue,
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
use ruff_python_ast::{ParameterWithDefault, Parameters, Ranged};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
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::{ParameterWithDefault, Parameters, Ranged, Stmt};
|
||||
use ruff_python_parser::lexer::lex_starts_at;
|
||||
use ruff_python_parser::{Mode, Tok};
|
||||
use ruff_python_semantic::analyze::typing::{is_immutable_annotation, is_mutable_expr};
|
||||
use ruff_python_trivia::indentation_at_offset;
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of mutable objects as function argument defaults.
|
||||
|
@ -50,14 +56,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")
|
||||
}
|
||||
fn autofix_title(&self) -> Option<String> {
|
||||
Some(format!(
|
||||
"Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// B006
|
||||
pub(crate) fn mutable_argument_default(checker: &mut Checker, parameters: &Parameters) {
|
||||
pub(crate) fn mutable_argument_default(
|
||||
checker: &mut Checker,
|
||||
parameters: &Parameters,
|
||||
body: &[Stmt],
|
||||
func_range: TextRange,
|
||||
) {
|
||||
// Scan in reverse order to right-align zip().
|
||||
for ParameterWithDefault {
|
||||
parameter,
|
||||
|
@ -79,9 +97,53 @@ pub(crate) fn mutable_argument_default(checker: &mut Checker, parameters: &Param
|
|||
.as_ref()
|
||||
.is_some_and(|expr| 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(body[0].start(), checker.locator()).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", parameter.name.as_str()).as_str());
|
||||
check_lines.push_str(indentation);
|
||||
check_lines.push_str(checker.stylist().indentation());
|
||||
check_lines.push_str(
|
||||
format!(
|
||||
"{} = {}",
|
||||
parameter.name.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
|
||||
}
|
||||
|
|
|
@ -1,123 +1,376 @@
|
|||
---
|
||||
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 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
63 | def this_is_wrong(value=[1, 2, 3]):
|
||||
| ^^^^^^^^^ B006
|
||||
64 | ...
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
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 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
67 | def this_is_also_wrong(value={}):
|
||||
| ^^ B006
|
||||
68 | ...
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
B006_B008.py:71:20: B006 Do not use mutable data structures for argument defaults
|
||||
|
|
||||
71 | def and_this(value=set()):
|
||||
| ^^^^^ B006
|
||||
72 | ...
|
||||
|
|
||||
ℹ 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:75:20: B006 Do not use mutable data structures for argument defaults
|
||||
B006_B008.py:73:52: B006 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
75 | def this_too(value=collections.OrderedDict()):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ B006
|
||||
76 | ...
|
||||
71 | class Foo:
|
||||
72 | @staticmethod
|
||||
73 | def this_is_also_wrong_and_more_indented(value={}):
|
||||
| ^^ B006
|
||||
74 | pass
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
B006_B008.py:79:32: 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 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
77 | def multiline_arg_wrong(value={
|
||||
| _______________________________^
|
||||
78 | |
|
||||
79 | | }):
|
||||
| |_^ B006
|
||||
80 | ...
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
ℹ 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 Do not use mutable data structures for argument defaults
|
||||
|
|
||||
79 | async def async_this_too(value=collections.defaultdict()):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ B006
|
||||
80 | ...
|
||||
81 |
|
||||
82 | def single_line_func_wrong(value = {}): ...
|
||||
| ^^ B006
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
B006_B008.py:83:26: B006 Do not use mutable data structures for argument defaults
|
||||
B006_B008.py:85:20: B006 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
83 | def dont_forget_me(value=collections.deque()):
|
||||
85 | def and_this(value=set()):
|
||||
| ^^^^^ B006
|
||||
86 | ...
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
ℹ 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 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
89 | def this_too(value=collections.OrderedDict()):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ B006
|
||||
90 | ...
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
ℹ 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 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
93 | async def async_this_too(value=collections.defaultdict()):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ B006
|
||||
94 | ...
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
ℹ 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 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
97 | def dont_forget_me(value=collections.deque()):
|
||||
| ^^^^^^^^^^^^^^^^^^^ B006
|
||||
84 | ...
|
||||
98 | ...
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
B006_B008.py:88:46: B006 Do not use mutable data structures for 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)]):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ B006
|
||||
89 | pass
|
||||
|
|
||||
ℹ 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:92:46: B006 Do not use mutable data structures for argument defaults
|
||||
|
|
||||
92 | def dict_comprehension_also_not_okay(default={i: i**2 for i in range(3)}):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ B006
|
||||
93 | pass
|
||||
|
|
||||
|
||||
B006_B008.py:96:45: B006 Do not use mutable data structures for argument defaults
|
||||
|
|
||||
96 | def set_comprehension_also_not_okay(default={i**2 for i in range(3)}):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ B006
|
||||
97 | pass
|
||||
|
|
||||
|
||||
B006_B008.py:100:33: B006 Do not use mutable data structures for argument defaults
|
||||
B006_B008.py:102:46: B006 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
100 | def kwonlyargs_mutable(*, value=[]):
|
||||
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
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
ℹ 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 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
106 | def dict_comprehension_also_not_okay(default={i: i**2 for i in range(3)}):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ B006
|
||||
107 | pass
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
ℹ 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 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
110 | def set_comprehension_also_not_okay(default={i**2 for i in range(3)}):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ B006
|
||||
111 | pass
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
ℹ 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 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
114 | def kwonlyargs_mutable(*, value=[]):
|
||||
| ^^ B006
|
||||
101 | ...
|
||||
115 | ...
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
B006_B008.py:221:20: B006 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:235:20: B006 [*] Do not use mutable data structures for 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()]):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B006
|
||||
222 | pass
|
||||
236 | pass
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
B006_B008.py:258: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:272:27: B006 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
257 | def mutable_annotations(
|
||||
258 | a: list[int] | None = [],
|
||||
271 | def mutable_annotations(
|
||||
272 | a: list[int] | None = [],
|
||||
| ^^ B006
|
||||
259 | b: Optional[Dict[int, int]] = {},
|
||||
260 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
273 | b: Optional[Dict[int, int]] = {},
|
||||
274 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
B006_B008.py:259:35: B006 Do not use mutable data structures for argument defaults
|
||||
ℹ Possible fix
|
||||
269 269 |
|
||||
270 270 |
|
||||
271 271 | def mutable_annotations(
|
||||
272 |- a: list[int] | None = [],
|
||||
272 |+ a: list[int] | None = None,
|
||||
273 273 | b: Optional[Dict[int, int]] = {},
|
||||
274 274 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
275 275 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
276 276 | ):
|
||||
277 |+ if a is None:
|
||||
278 |+ a = []
|
||||
277 279 | pass
|
||||
|
||||
B006_B008.py:273:35: B006 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
257 | def mutable_annotations(
|
||||
258 | a: list[int] | None = [],
|
||||
259 | b: Optional[Dict[int, int]] = {},
|
||||
271 | def mutable_annotations(
|
||||
272 | a: list[int] | None = [],
|
||||
273 | b: Optional[Dict[int, int]] = {},
|
||||
| ^^ B006
|
||||
260 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
261 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
274 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
275 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
B006_B008.py:260:62: B006 Do not use mutable data structures for argument defaults
|
||||
ℹ Possible fix
|
||||
270 270 |
|
||||
271 271 | def mutable_annotations(
|
||||
272 272 | a: list[int] | None = [],
|
||||
273 |- b: Optional[Dict[int, int]] = {},
|
||||
273 |+ b: Optional[Dict[int, int]] = None,
|
||||
274 274 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
275 275 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
276 276 | ):
|
||||
277 |+ if b is None:
|
||||
278 |+ b = {}
|
||||
277 279 | pass
|
||||
|
||||
B006_B008.py:274:62: B006 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
258 | a: list[int] | None = [],
|
||||
259 | b: Optional[Dict[int, int]] = {},
|
||||
260 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
272 | a: list[int] | None = [],
|
||||
273 | b: Optional[Dict[int, int]] = {},
|
||||
274 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
| ^^^^^ B006
|
||||
261 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
262 | ):
|
||||
275 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
276 | ):
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
B006_B008.py:261:80: B006 Do not use mutable data structures for argument defaults
|
||||
ℹ Possible fix
|
||||
271 271 | def mutable_annotations(
|
||||
272 272 | a: list[int] | None = [],
|
||||
273 273 | b: Optional[Dict[int, int]] = {},
|
||||
274 |- c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
274 |+ c: Annotated[Union[Set[str], abc.Sized], "annotation"] = None,
|
||||
275 275 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
276 276 | ):
|
||||
277 |+ if c is None:
|
||||
278 |+ c = set()
|
||||
277 279 | pass
|
||||
|
||||
B006_B008.py:275:80: B006 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
259 | b: Optional[Dict[int, int]] = {},
|
||||
260 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
261 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
273 | b: Optional[Dict[int, int]] = {},
|
||||
274 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
275 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
| ^^^^^ B006
|
||||
262 | ):
|
||||
263 | pass
|
||||
276 | ):
|
||||
277 | pass
|
||||
|
|
||||
= help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`
|
||||
|
||||
ℹ Possible fix
|
||||
272 272 | a: list[int] | None = [],
|
||||
273 273 | b: Optional[Dict[int, int]] = {},
|
||||
274 274 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
275 |- d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
275 |+ d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = None,
|
||||
276 276 | ):
|
||||
277 |+ if d is None:
|
||||
278 |+ d = set()
|
||||
277 279 | pass
|
||||
|
||||
|
||||
|
|
|
@ -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: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: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:112:39: B008 Do not perform function call `time.time` in argument defaults
|
||||
B006_B008.py:102:61: B008 Do not perform function call `range` 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()):
|
||||
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: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: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:126:39: B008 Do not perform function call `time.time` in argument defaults
|
||||
|
|
||||
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
|
||||
|
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue