Skip to content

Commit 64793c0

Browse files
committed
Exempt unittest context methods for SIM115 rule
1 parent 09d0b22 commit 64793c0

File tree

3 files changed

+299
-250
lines changed

3 files changed

+299
-250
lines changed

crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM115.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pathlib as pl
44
from pathlib import Path
55
from pathlib import Path as P
6+
from unittest import IsolatedAsyncioTestCase, TestCase
67

78
# SIM115
89
f = open("foo.txt")
@@ -59,6 +60,25 @@ def __exit__(self, exc_type, exc_val, exc_tb):
5960
self.file.close()
6061

6162

63+
# OK
64+
class ExampleClassTests(TestCase):
65+
@classmethod
66+
def setUpClass(cls):
67+
cls.enterClassContext(open("filename"))
68+
69+
70+
# OK
71+
class ExampleAsyncTests(IsolatedAsyncioTestCase):
72+
async def test_something(self):
73+
await self.enterAsyncContext(open("filename"))
74+
75+
76+
# OK
77+
class ExampleTests(TestCase):
78+
def setUp(self):
79+
self.enterContext(open("filename"))
80+
81+
6282
import tempfile
6383
import tarfile
6484
from tarfile import TarFile

crates/ruff_linter/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,30 @@ fn match_exit_stack(semantic: &SemanticModel) -> bool {
114114
false
115115
}
116116

117+
/// Return `true` if the current expression is nested in a call to one of the
118+
/// unittest context manager methods: `cls.enterClassContext()`,
119+
/// `self.enterContext()`, or `self.enterAsyncContext()`.
120+
fn match_unittest_context_methods(semantic: &SemanticModel) -> bool {
121+
let Some(expr) = semantic.current_expression_parent() else {
122+
return false;
123+
};
124+
let Expr::Call(ast::ExprCall { func, .. }) = expr else {
125+
return false;
126+
};
127+
let Expr::Attribute(ast::ExprAttribute { attr, value, .. }) = func.as_ref() else {
128+
return false;
129+
};
130+
if attr != "enterClassContext" && attr != "enterContext" && attr != "enterAsyncContext" {
131+
return false;
132+
}
133+
let Expr::Name(ast::ExprName { id, .. }) = value.as_ref() else {
134+
return false;
135+
};
136+
id == "cls" && attr == "enterClassContext"
137+
|| id == "self" && attr == "enterContext"
138+
|| id == "self" && attr == "enterAsyncContext"
139+
}
140+
117141
/// Return `true` if the expression is a call to `open()`,
118142
/// or a call to some other standard-library function that opens a file.
119143
fn is_open_call(semantic: &SemanticModel, call: &ast::ExprCall) -> bool {
@@ -229,6 +253,11 @@ pub(crate) fn open_file_with_context_handler(checker: &Checker, call: &ast::Expr
229253
return;
230254
}
231255

256+
// Ex) `self.enterContext(open("foo.txt"))`
257+
if match_unittest_context_methods(semantic) {
258+
return;
259+
}
260+
232261
// Ex) `def __enter__(self): ...`
233262
if let ScopeKind::Function(ast::StmtFunctionDef { name, .. }) =
234263
&checker.semantic().current_scope().kind

0 commit comments

Comments
 (0)