mirror of https://github.com/astral-sh/ruff
Add tests, clarify docs
This commit is contained in:
parent
14b55f6504
commit
67a49f2c32
|
|
@ -3,7 +3,7 @@ MY_SET = {"ABC", "DEF"} # plain set
|
||||||
MY_LIST = [1, 2, 3] # plain list
|
MY_LIST = [1, 2, 3] # plain list
|
||||||
MY_DICT = {"key": "value"} # plain dict
|
MY_DICT = {"key": "value"} # plain dict
|
||||||
|
|
||||||
# NOT triggering B006 (correct - UPPER_CASE constants are excluded per PEP 8)
|
# NOT triggering B006 (correct - PEP 8 specifies that UPPER_CASE variables are considered constants)
|
||||||
def func_A(s: set[str] = MY_SET):
|
def func_A(s: set[str] = MY_SET):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
@ -11,11 +11,11 @@ def func_A(s: set[str] = MY_SET):
|
||||||
def func_B(s: set[str] = {"ABC", "DEF"}):
|
def func_B(s: set[str] = {"ABC", "DEF"}):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
# NOT triggering B006 (correct - UPPER_CASE constants are excluded per PEP 8)
|
# NOT triggering B006 (correct - PEP 8 specifies that UPPER_CASE variables are considered constants)
|
||||||
def func_C(items: list[int] = MY_LIST):
|
def func_C(items: list[int] = MY_LIST):
|
||||||
return items
|
return items
|
||||||
|
|
||||||
# NOT triggering B006 (correct - UPPER_CASE constants are excluded per PEP 8)
|
# NOT triggering B006 (correct - PEP 8 specifies that UPPER_CASE variables are considered constants)
|
||||||
def func_D(data: dict[str, str] = MY_DICT):
|
def func_D(data: dict[str, str] = MY_DICT):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Test nested function reading value from outer scope
|
||||||
|
def outer_function():
|
||||||
|
my_list = [1, 2, 3] # Assignment in outer function
|
||||||
|
|
||||||
|
# Should trigger B006 - nested function using mutable from outer scope
|
||||||
|
def inner_function(items=my_list):
|
||||||
|
return items
|
||||||
|
|
||||||
|
return inner_function
|
||||||
|
|
||||||
|
|
||||||
|
# Test that imports don't trigger B006 (assignment restriction)
|
||||||
|
from some_module import IMPORTED_LIST
|
||||||
|
|
||||||
|
# Should NOT trigger B006 - imported names are not assignments
|
||||||
|
def func_with_import(items=IMPORTED_LIST):
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
# Test that function parameters don't trigger B006 (assignment restriction)
|
||||||
|
def func_with_param(param_list):
|
||||||
|
# Should NOT trigger B006 - function parameters are not assignments
|
||||||
|
def nested_func(items=param_list):
|
||||||
|
return items
|
||||||
|
|
||||||
|
return nested_func
|
||||||
|
|
||||||
|
|
||||||
|
# Test module-level assignment that should trigger B006
|
||||||
|
module_list = [1, 2, 3] # Module-level assignment
|
||||||
|
|
||||||
|
# Should trigger B006 - module-level assignment used as default
|
||||||
|
def func_with_module_var(items=module_list):
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
# Test that non-assignment bindings don't trigger B006
|
||||||
|
# (This would require a more complex setup to actually test, so we'll focus on the key cases above)
|
||||||
|
|
||||||
|
|
@ -49,6 +49,7 @@ mod tests {
|
||||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_8.py"))]
|
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_8.py"))]
|
||||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_9.py"))]
|
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_9.py"))]
|
||||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_10.py"))]
|
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_10.py"))]
|
||||||
|
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_11.py"))]
|
||||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_B008.py"))]
|
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_B008.py"))]
|
||||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_1.pyi"))]
|
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_1.pyi"))]
|
||||||
#[test_case(Rule::NoExplicitStacklevel, Path::new("B028.py"))]
|
#[test_case(Rule::NoExplicitStacklevel, Path::new("B028.py"))]
|
||||||
|
|
@ -95,6 +96,7 @@ mod tests {
|
||||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_8.py"))]
|
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_8.py"))]
|
||||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_9.py"))]
|
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_9.py"))]
|
||||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_10.py"))]
|
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_10.py"))]
|
||||||
|
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_11.py"))]
|
||||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_B008.py"))]
|
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_B008.py"))]
|
||||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_1.pyi"))]
|
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_1.pyi"))]
|
||||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -150,12 +150,15 @@ fn is_guaranteed_mutable_expr(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||||
if str::is_cased_uppercase(&name.id) {
|
if str::is_cased_uppercase(&name.id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Resolve module-level constants that are bound to mutable objects
|
// Resolve the name in the current scope (module-level, function-level, etc.)
|
||||||
|
// This can resolve constants, non-constants, and any name in the current scope
|
||||||
let Some(binding_id) = semantic.only_binding(name) else {
|
let Some(binding_id) = semantic.only_binding(name) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let binding = semantic.binding(binding_id);
|
let binding = semantic.binding(binding_id);
|
||||||
// Only check assignments (not imports, function parameters, etc.)
|
// Only check assignments (not imports, function parameters, etc.)
|
||||||
|
// This restriction ensures we only flag cases where a mutable object is explicitly
|
||||||
|
// assigned to a name, which is then used as a default argument.
|
||||||
if !binding.kind.is_assignment() {
|
if !binding.kind.is_assignment() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,5 +19,5 @@ help: Replace with `None`; initialize within function
|
||||||
13 + s = {"ABC", "DEF"}
|
13 + s = {"ABC", "DEF"}
|
||||||
14 | return s
|
14 | return s
|
||||||
15 |
|
15 |
|
||||||
16 | # NOT triggering B006 (correct - UPPER_CASE constants are excluded per PEP 8)
|
16 | # NOT triggering B006 (correct - PEP 8 specifies that UPPER_CASE variables are considered constants)
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs
|
||||||
|
---
|
||||||
|
|
||||||
|
|
@ -19,5 +19,5 @@ help: Replace with `None`; initialize within function
|
||||||
13 + s = {"ABC", "DEF"}
|
13 + s = {"ABC", "DEF"}
|
||||||
14 | return s
|
14 | return s
|
||||||
15 |
|
15 |
|
||||||
16 | # NOT triggering B006 (correct - UPPER_CASE constants are excluded per PEP 8)
|
16 | # NOT triggering B006 (correct - PEP 8 specifies that UPPER_CASE variables are considered constants)
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs
|
||||||
|
---
|
||||||
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
|
--> B006_11.py:6:30
|
||||||
|
|
|
||||||
|
5 | # Should trigger B006 - nested function using mutable from outer scope
|
||||||
|
6 | def inner_function(items=my_list):
|
||||||
|
| ^^^^^^^
|
||||||
|
7 | return items
|
||||||
|
|
|
||||||
|
help: Replace with `None`; initialize within function
|
||||||
|
3 | my_list = [1, 2, 3] # Assignment in outer function
|
||||||
|
4 |
|
||||||
|
5 | # Should trigger B006 - nested function using mutable from outer scope
|
||||||
|
- def inner_function(items=my_list):
|
||||||
|
6 + def inner_function(items=None):
|
||||||
|
7 + if items is None:
|
||||||
|
8 + items = my_list
|
||||||
|
9 | return items
|
||||||
|
10 |
|
||||||
|
11 | return inner_function
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
|
--> B006_11.py:33:32
|
||||||
|
|
|
||||||
|
32 | # Should trigger B006 - module-level assignment used as default
|
||||||
|
33 | def func_with_module_var(items=module_list):
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
34 | return items
|
||||||
|
|
|
||||||
|
help: Replace with `None`; initialize within function
|
||||||
|
30 | module_list = [1, 2, 3] # Module-level assignment
|
||||||
|
31 |
|
||||||
|
32 | # Should trigger B006 - module-level assignment used as default
|
||||||
|
- def func_with_module_var(items=module_list):
|
||||||
|
33 + def func_with_module_var(items=None):
|
||||||
|
34 + if items is None:
|
||||||
|
35 + items = module_list
|
||||||
|
36 | return items
|
||||||
|
37 |
|
||||||
|
38 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
Loading…
Reference in New Issue