Implement autofix for PT009 (#1713)

This commit is contained in:
Harutaka Kawamura 2023-01-08 02:28:25 +09:00 committed by GitHub
parent 402feffe85
commit 07f72990a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 946 additions and 56 deletions

View file

@ -919,7 +919,7 @@ For more, see [flake8-pytest-style](https://pypi.org/project/flake8-pytest-style
| PT006 | ParametrizeNamesWrongType | Wrong name(s) type in `@pytest.mark.parametrize`, expected `tuple` | 🛠 |
| PT007 | ParametrizeValuesWrongType | Wrong values type in `@pytest.mark.parametrize` expected `list` of `tuple` | |
| PT008 | PatchWithLambda | Use `return_value=` instead of patching with `lambda` | |
| PT009 | UnittestAssertion | Use a regular `assert` instead of unittest-style `...` | |
| PT009 | UnittestAssertion | Use a regular `assert` instead of unittest-style `...` | 🛠 |
| PT010 | RaisesWithoutException | set the expected exception in `pytest.raises()` | |
| PT011 | RaisesTooBroad | `pytest.raises(...)` is too broad, set the `match` parameter or use a more specific exception | |
| PT012 | RaisesWithMultipleStatements | `pytest.raises()` block should contain a single simple statement | |

View file

@ -1,9 +1,76 @@
import pytest
import unittest
def test_xxx():
assert 1 == 1 # OK no parameters
class Test(unittest.TestCase):
def test_xxx(self):
assert 1 == 1 # OK no parameters
def test_assert_true(self):
expr = 1
msg = "Must be True"
self.assertTrue(expr) # Error
self.assertTrue(expr=expr) # Error
self.assertTrue(expr, msg) # Error
self.assertTrue(expr=expr, msg=msg) # Error
self.assertTrue(msg=msg, expr=expr) # Error
self.assertTrue(*(expr, msg)) # Error, unfixable
self.assertTrue(**{"expr": expr, "msg": msg}) # Error, unfixable
self.assertTrue(msg=msg, expr=expr, unexpected_arg=False) # Error, unfixable
self.assertTrue(msg=msg) # Error, unfixable
def test_xxx():
self.assertEqual(1, 1) # Error
def test_assert_false(self):
self.assertFalse(True) # Error
def test_assert_equal(self):
self.assertEqual(1, 2) # Error
def test_assert_not_equal(self):
self.assertNotEqual(1, 1) # Error
def test_assert_greater(self):
self.assertGreater(1, 2) # Error
def test_assert_greater_equal(self):
self.assertGreaterEqual(1, 2) # Error
def test_assert_less(self):
self.assertLess(2, 1) # Error
def test_assert_less_equal(self):
self.assertLessEqual(1, 2) # Error
def test_assert_in(self):
self.assertIn(1, [2, 3]) # Error
def test_assert_not_in(self):
self.assertNotIn(2, [2, 3]) # Error
def test_assert_is_none(self):
self.assertIsNone(0) # Error
def test_assert_is_not_none(self):
self.assertIsNotNone(0) # Error
def test_assert_is(self):
self.assertIs([], []) # Error
def test_assert_is_not(self):
self.assertIsNot(1, 1) # Error
def test_assert_is_instance(self):
self.assertIsInstance(1, str) # Error
def test_assert_is_not_instance(self):
self.assertNotIsInstance(1, int) # Error
def test_assert_regex(self):
self.assertRegex("abc", r"def") # Error
def test_assert_not_regex(self):
self.assertNotRegex("abc", r"abc") # Error
def test_assert_regexp_matches(self):
self.assertRegexpMatches("abc", r"def") # Error
def test_assert_not_regexp_matches(self):
self.assertNotRegex("abc", r"abc") # Error

View file

@ -2355,7 +2355,9 @@ where
}
}
if self.settings.enabled.contains(&CheckCode::PT009) {
if let Some(check) = flake8_pytest_style::plugins::unittest_assertion(func) {
if let Some(check) = flake8_pytest_style::plugins::unittest_assertion(
self, expr, func, args, keywords,
) {
self.checks.push(check);
}
}

View file

@ -1,11 +1,15 @@
use rustpython_ast::{
Boolop, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Stmt, StmtKind, Unaryop,
Boolop, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Keyword, Stmt, StmtKind, Unaryop,
};
use super::helpers::is_falsy_constant;
use super::unittest_assert::UnittestAssert;
use crate::ast::helpers::unparse_stmt;
use crate::ast::types::Range;
use crate::ast::visitor;
use crate::ast::visitor::Visitor;
use crate::autofix::Fix;
use crate::checkers::ast::Checker;
use crate::registry::{Check, CheckKind};
/// Visitor that tracks assert statements and checks if they reference
@ -58,42 +62,6 @@ where
}
}
const UNITTEST_ASSERT_NAMES: &[&str] = &[
"assertAlmostEqual",
"assertAlmostEquals",
"assertDictEqual",
"assertEqual",
"assertEquals",
"assertFalse",
"assertGreater",
"assertGreaterEqual",
"assertIn",
"assertIs",
"assertIsInstance",
"assertIsNone",
"assertIsNot",
"assertIsNotNone",
"assertItemsEqual",
"assertLess",
"assertLessEqual",
"assertMultiLineEqual",
"assertNotAlmostEqual",
"assertNotAlmostEquals",
"assertNotContains",
"assertNotEqual",
"assertNotEquals",
"assertNotIn",
"assertNotIsInstance",
"assertNotRegexpMatches",
"assertRaises",
"assertRaisesMessage",
"assertRaisesRegexp",
"assertRegexpMatches",
"assertSetEqual",
"assertTrue",
"assert_",
];
/// Check if the test expression is a composite condition.
/// For example, `a and b` or `not (a or b)`. The latter is equivalent
/// to `not a and not b` by De Morgan's laws.
@ -120,14 +88,30 @@ fn check_assert_in_except(name: &str, body: &[Stmt]) -> Vec<Check> {
}
/// PT009
pub fn unittest_assertion(call: &Expr) -> Option<Check> {
match &call.node {
pub fn unittest_assertion(
checker: &Checker,
call: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) -> Option<Check> {
match &func.node {
ExprKind::Attribute { attr, .. } => {
if UNITTEST_ASSERT_NAMES.contains(&attr.as_str()) {
Some(Check::new(
CheckKind::UnittestAssertion(attr.to_string()),
Range::from_located(call),
))
if let Ok(unittest_assert) = UnittestAssert::try_from(attr.as_str()) {
let mut check = Check::new(
CheckKind::UnittestAssertion(unittest_assert.to_string()),
Range::from_located(func),
);
if checker.patch(check.kind.code()) {
if let Ok(stmt) = unittest_assert.generate_assert(args, keywords) {
check.amend(Fix::replacement(
unparse_stmt(&stmt, checker.style),
call.location,
call.end_location.unwrap(),
));
}
}
Some(check)
} else {
None
}

View file

@ -18,3 +18,4 @@ mod marks;
mod parametrize;
mod patch;
mod raises;
mod unittest_assert;

View file

@ -0,0 +1,394 @@
use std::hash::BuildHasherDefault;
use anyhow::{anyhow, bail, Result};
use rustc_hash::FxHashMap;
use rustpython_ast::{
Cmpop, Constant, Expr, ExprContext, ExprKind, Keyword, Stmt, StmtKind, Unaryop,
};
use crate::ast::helpers::{create_expr, create_stmt};
pub enum UnittestAssert {
AlmostEqual,
AlmostEquals,
DictEqual,
Equal,
Equals,
False,
Greater,
GreaterEqual,
In,
Is,
IsInstance,
IsNone,
IsNot,
IsNotNone,
ItemsEqual,
Less,
LessEqual,
MultiLineEqual,
NotAlmostEqual,
NotAlmostEquals,
NotContains,
NotEqual,
NotEquals,
NotIn,
NotIsInstance,
NotRegex,
NotRegexpMatches,
Raises,
RaisesMessage,
RaisesRegexp,
Regex,
RegexpMatches,
SetEqual,
True,
Underscore,
}
impl std::fmt::Display for UnittestAssert {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UnittestAssert::AlmostEqual => write!(f, "assertAlmostEqual"),
UnittestAssert::AlmostEquals => write!(f, "assertAlmostEquals"),
UnittestAssert::DictEqual => write!(f, "assertDictEqual"),
UnittestAssert::Equal => write!(f, "assertEqual"),
UnittestAssert::Equals => write!(f, "assertEquals"),
UnittestAssert::False => write!(f, "assertFalse"),
UnittestAssert::Greater => write!(f, "assertGreater"),
UnittestAssert::GreaterEqual => write!(f, "assertGreaterEqual"),
UnittestAssert::In => write!(f, "assertIn"),
UnittestAssert::Is => write!(f, "assertIs"),
UnittestAssert::IsInstance => write!(f, "assertIsInstance"),
UnittestAssert::IsNone => write!(f, "assertIsNone"),
UnittestAssert::IsNot => write!(f, "assertIsNot"),
UnittestAssert::IsNotNone => write!(f, "assertIsNotNone"),
UnittestAssert::ItemsEqual => write!(f, "assertItemsEqual"),
UnittestAssert::Less => write!(f, "assertLess"),
UnittestAssert::LessEqual => write!(f, "assertLessEqual"),
UnittestAssert::MultiLineEqual => write!(f, "assertMultiLineEqual"),
UnittestAssert::NotAlmostEqual => write!(f, "assertNotAlmostEqual"),
UnittestAssert::NotAlmostEquals => write!(f, "assertNotAlmostEquals"),
UnittestAssert::NotContains => write!(f, "assertNotContains"),
UnittestAssert::NotEqual => write!(f, "assertNotEqual"),
UnittestAssert::NotEquals => write!(f, "assertNotEquals"),
UnittestAssert::NotIn => write!(f, "assertNotIn"),
UnittestAssert::NotIsInstance => write!(f, "assertNotIsInstance"),
UnittestAssert::NotRegex => write!(f, "assertNotRegex"),
UnittestAssert::NotRegexpMatches => write!(f, "assertNotRegexpMatches"),
UnittestAssert::Raises => write!(f, "assertRaises"),
UnittestAssert::RaisesMessage => write!(f, "assertRaisesMessage"),
UnittestAssert::RaisesRegexp => write!(f, "assertRaisesRegexp"),
UnittestAssert::Regex => write!(f, "assertRegex"),
UnittestAssert::RegexpMatches => write!(f, "assertRegexpMatches"),
UnittestAssert::SetEqual => write!(f, "assertSetEqual"),
UnittestAssert::True => write!(f, "assertTrue"),
UnittestAssert::Underscore => write!(f, "assert_"),
}
}
}
impl TryFrom<&str> for UnittestAssert {
type Error = String;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"assertAlmostEqual" => Ok(UnittestAssert::AlmostEqual),
"assertAlmostEquals" => Ok(UnittestAssert::AlmostEquals),
"assertDictEqual" => Ok(UnittestAssert::DictEqual),
"assertEqual" => Ok(UnittestAssert::Equal),
"assertEquals" => Ok(UnittestAssert::Equals),
"assertFalse" => Ok(UnittestAssert::False),
"assertGreater" => Ok(UnittestAssert::Greater),
"assertGreaterEqual" => Ok(UnittestAssert::GreaterEqual),
"assertIn" => Ok(UnittestAssert::In),
"assertIs" => Ok(UnittestAssert::Is),
"assertIsInstance" => Ok(UnittestAssert::IsInstance),
"assertIsNone" => Ok(UnittestAssert::IsNone),
"assertIsNot" => Ok(UnittestAssert::IsNot),
"assertIsNotNone" => Ok(UnittestAssert::IsNotNone),
"assertItemsEqual" => Ok(UnittestAssert::ItemsEqual),
"assertLess" => Ok(UnittestAssert::Less),
"assertLessEqual" => Ok(UnittestAssert::LessEqual),
"assertMultiLineEqual" => Ok(UnittestAssert::MultiLineEqual),
"assertNotAlmostEqual" => Ok(UnittestAssert::NotAlmostEqual),
"assertNotAlmostEquals" => Ok(UnittestAssert::NotAlmostEquals),
"assertNotContains" => Ok(UnittestAssert::NotContains),
"assertNotEqual" => Ok(UnittestAssert::NotEqual),
"assertNotEquals" => Ok(UnittestAssert::NotEquals),
"assertNotIn" => Ok(UnittestAssert::NotIn),
"assertNotIsInstance" => Ok(UnittestAssert::NotIsInstance),
"assertNotRegex" => Ok(UnittestAssert::NotRegex),
"assertNotRegexpMatches" => Ok(UnittestAssert::NotRegexpMatches),
"assertRaises" => Ok(UnittestAssert::Raises),
"assertRaisesMessage" => Ok(UnittestAssert::RaisesMessage),
"assertRaisesRegexp" => Ok(UnittestAssert::RaisesRegexp),
"assertRegex" => Ok(UnittestAssert::Regex),
"assertRegexpMatches" => Ok(UnittestAssert::RegexpMatches),
"assertSetEqual" => Ok(UnittestAssert::SetEqual),
"assertTrue" => Ok(UnittestAssert::True),
"assert_" => Ok(UnittestAssert::Underscore),
_ => Err(format!("Unknown unittest assert method: {value}")),
}
}
}
fn assert(expr: &Expr, msg: Option<&Expr>) -> Stmt {
create_stmt(StmtKind::Assert {
test: Box::new(expr.clone()),
msg: msg.map(|msg| Box::new(msg.clone())),
})
}
fn compare(left: &Expr, cmpop: Cmpop, right: &Expr) -> Expr {
create_expr(ExprKind::Compare {
left: Box::new(left.clone()),
ops: vec![cmpop],
comparators: vec![right.clone()],
})
}
pub struct Arguments<'a> {
positional: Vec<&'a str>,
keyword: Vec<&'a str>,
}
impl<'a> Arguments<'a> {
pub fn new(positional: Vec<&'a str>, keyword: Vec<&'a str>) -> Self {
Self {
positional,
keyword,
}
}
pub fn contains(&self, arg: &str) -> bool {
self.positional.contains(&arg) || self.keyword.contains(&arg)
}
}
impl UnittestAssert {
pub fn arguments(&self) -> Arguments {
match self {
UnittestAssert::AlmostEqual => {
Arguments::new(vec!["first", "second"], vec!["places", "msg", "delta"])
}
UnittestAssert::AlmostEquals => {
Arguments::new(vec!["first", "second"], vec!["places", "msg", "delta"])
}
UnittestAssert::DictEqual => Arguments::new(vec!["d1", "d2"], vec!["msg"]),
UnittestAssert::Equal => Arguments::new(vec!["first", "second"], vec!["msg"]),
UnittestAssert::Equals => Arguments::new(vec!["first", "second"], vec!["msg"]),
UnittestAssert::False => Arguments::new(vec!["expr"], vec!["msg"]),
UnittestAssert::Greater => Arguments::new(vec!["first", "second"], vec!["msg"]),
UnittestAssert::GreaterEqual => Arguments::new(vec!["first", "second"], vec!["msg"]),
UnittestAssert::In => Arguments::new(vec!["member", "container"], vec!["msg"]),
UnittestAssert::Is => Arguments::new(vec!["expr1", "expr2"], vec!["msg"]),
UnittestAssert::IsInstance => Arguments::new(vec!["obj", "cls"], vec!["msg"]),
UnittestAssert::IsNone => Arguments::new(vec!["expr"], vec!["msg"]),
UnittestAssert::IsNot => Arguments::new(vec!["expr1", "expr2"], vec!["msg"]),
UnittestAssert::IsNotNone => Arguments::new(vec!["expr"], vec!["msg"]),
UnittestAssert::ItemsEqual => Arguments::new(vec!["first", "second"], vec!["msg"]),
UnittestAssert::Less => Arguments::new(vec!["first", "second"], vec!["msg"]),
UnittestAssert::LessEqual => Arguments::new(vec!["first", "second"], vec!["msg"]),
UnittestAssert::MultiLineEqual => Arguments::new(vec!["first", "second"], vec!["msg"]),
UnittestAssert::NotAlmostEqual => Arguments::new(vec!["first", "second"], vec!["msg"]),
UnittestAssert::NotAlmostEquals => Arguments::new(vec!["first", "second"], vec!["msg"]),
UnittestAssert::NotContains => Arguments::new(vec!["container", "member"], vec!["msg"]),
UnittestAssert::NotEqual => Arguments::new(vec!["first", "second"], vec!["msg"]),
UnittestAssert::NotEquals => Arguments::new(vec!["first", "second"], vec!["msg"]),
UnittestAssert::NotIn => Arguments::new(vec!["member", "container"], vec!["msg"]),
UnittestAssert::NotIsInstance => Arguments::new(vec!["obj", "cls"], vec!["msg"]),
UnittestAssert::NotRegex => Arguments::new(vec!["text", "regex"], vec!["msg"]),
UnittestAssert::NotRegexpMatches => Arguments::new(vec!["text", "regex"], vec!["msg"]),
UnittestAssert::Raises => Arguments::new(vec!["exception"], vec!["msg"]),
UnittestAssert::RaisesMessage => Arguments::new(vec!["exception", "msg"], vec!["msg"]),
UnittestAssert::RaisesRegexp => Arguments::new(vec!["exception", "regex"], vec!["msg"]),
UnittestAssert::Regex => Arguments::new(vec!["text", "regex"], vec!["msg"]),
UnittestAssert::RegexpMatches => Arguments::new(vec!["text", "regex"], vec!["msg"]),
UnittestAssert::SetEqual => Arguments::new(vec!["set1", "set2"], vec!["msg"]),
UnittestAssert::True => Arguments::new(vec!["expr"], vec!["msg"]),
UnittestAssert::Underscore => Arguments::new(vec!["expr"], vec!["msg"]),
}
}
/// Create a map from argument name to value.
pub fn args_map<'a>(
&'a self,
args: &'a [Expr],
keywords: &'a [Keyword],
) -> Result<FxHashMap<&'a str, &'a Expr>> {
if args
.iter()
.any(|arg| matches!(arg.node, ExprKind::Starred { .. }))
|| keywords.iter().any(|kw| kw.node.arg.is_none())
{
bail!("Contains variable-length arguments. Cannot autofix.".to_string());
}
let mut args_map: FxHashMap<&str, &Expr> = FxHashMap::with_capacity_and_hasher(
args.len() + keywords.len(),
BuildHasherDefault::default(),
);
let arguments = self.arguments();
for (arg, value) in arguments.positional.iter().zip(args.iter()) {
args_map.insert(arg, value);
}
for kw in keywords {
let arg = kw.node.arg.as_ref().unwrap();
if !arguments.contains((*arg).as_str()) {
bail!("Unexpected keyword argument `{arg}`");
}
args_map.insert(kw.node.arg.as_ref().unwrap().as_str(), &kw.node.value);
}
Ok(args_map)
}
pub fn generate_assert(&self, args: &[Expr], keywords: &[Keyword]) -> Result<Stmt> {
let args = self.args_map(args, keywords)?;
match self {
UnittestAssert::True | UnittestAssert::False => {
let expr = args.get("expr").ok_or(anyhow!("Missing argument `expr`"))?;
let msg = args.get("msg").copied();
let bool = create_expr(ExprKind::Constant {
value: Constant::Bool(matches!(self, UnittestAssert::True)),
kind: None,
});
let expr = compare(expr, Cmpop::Is, &bool);
Ok(assert(&expr, msg))
}
UnittestAssert::Equal
| UnittestAssert::Equals
| UnittestAssert::NotEqual
| UnittestAssert::NotEquals
| UnittestAssert::Greater
| UnittestAssert::GreaterEqual
| UnittestAssert::Less
| UnittestAssert::LessEqual => {
let first = args
.get("first")
.ok_or(anyhow!("Missing argument `first`"))?;
let second = args
.get("second")
.ok_or(anyhow!("Missing argument `second`"))?;
let msg = args.get("msg").copied();
let cmpop = match self {
UnittestAssert::Equal | UnittestAssert::Equals => Cmpop::Eq,
UnittestAssert::NotEqual | UnittestAssert::NotEquals => Cmpop::NotEq,
UnittestAssert::Greater => Cmpop::Gt,
UnittestAssert::GreaterEqual => Cmpop::GtE,
UnittestAssert::Less => Cmpop::Lt,
UnittestAssert::LessEqual => Cmpop::LtE,
_ => unreachable!(),
};
let expr = compare(first, cmpop, second);
Ok(assert(&expr, msg))
}
UnittestAssert::Is | UnittestAssert::IsNot => {
let expr1 = args
.get("expr1")
.ok_or(anyhow!("Missing argument `expr1`"))?;
let expr2 = args
.get("expr2")
.ok_or(anyhow!("Missing argument `expr2`"))?;
let msg = args.get("msg").copied();
let cmpop = if matches!(self, UnittestAssert::Is) {
Cmpop::Is
} else {
Cmpop::IsNot
};
let expr = compare(expr1, cmpop, expr2);
Ok(assert(&expr, msg))
}
UnittestAssert::In | UnittestAssert::NotIn => {
let member = args
.get("member")
.ok_or(anyhow!("Missing argument `member`"))?;
let container = args
.get("container")
.ok_or(anyhow!("Missing argument `container`"))?;
let msg = args.get("msg").copied();
let cmpop = if matches!(self, UnittestAssert::In) {
Cmpop::In
} else {
Cmpop::NotIn
};
let expr = compare(member, cmpop, container);
Ok(assert(&expr, msg))
}
UnittestAssert::IsNone | UnittestAssert::IsNotNone => {
let expr = args.get("expr").ok_or(anyhow!("Missing argument `expr`"))?;
let msg = args.get("msg").copied();
let cmpop = if matches!(self, UnittestAssert::IsNone) {
Cmpop::Is
} else {
Cmpop::IsNot
};
let expr = compare(
expr,
cmpop,
&create_expr(ExprKind::Constant {
value: Constant::None,
kind: None,
}),
);
Ok(assert(&expr, msg))
}
UnittestAssert::IsInstance | UnittestAssert::NotIsInstance => {
let obj = args.get("obj").ok_or(anyhow!("Missing argument `obj`"))?;
let cls = args.get("cls").ok_or(anyhow!("Missing argument `cls`"))?;
let msg = args.get("msg").copied();
let isinstance = create_expr(ExprKind::Call {
func: Box::new(create_expr(ExprKind::Name {
id: "isinstance".to_string(),
ctx: ExprContext::Load,
})),
args: vec![(**obj).clone(), (**cls).clone()],
keywords: vec![],
});
if matches!(self, UnittestAssert::IsInstance) {
Ok(assert(&isinstance, msg))
} else {
let expr = create_expr(ExprKind::UnaryOp {
op: Unaryop::Not,
operand: Box::new(isinstance),
});
Ok(assert(&expr, msg))
}
}
UnittestAssert::Regex
| UnittestAssert::RegexpMatches
| UnittestAssert::NotRegex
| UnittestAssert::NotRegexpMatches => {
let regex = args
.get("regex")
.ok_or(anyhow!("Missing argument `regex`"))?;
let text = args.get("text").ok_or(anyhow!("Missing argument `text`"))?;
let msg = args.get("msg").copied();
let re_search = create_expr(ExprKind::Call {
func: Box::new(create_expr(ExprKind::Attribute {
value: Box::new(create_expr(ExprKind::Name {
id: "re".to_string(),
ctx: ExprContext::Load,
})),
attr: "search".to_string(),
ctx: ExprContext::Load,
})),
args: vec![(**regex).clone(), (**text).clone()],
keywords: vec![],
});
if matches!(self, UnittestAssert::Regex | UnittestAssert::RegexpMatches) {
Ok(assert(&re_search, msg))
} else {
Ok(assert(
&create_expr(ExprKind::UnaryOp {
op: Unaryop::Not,
operand: Box::new(re_search),
}),
msg,
))
}
}
_ => bail!("Cannot autofix `{self}`"),
}
}
}

View file

@ -3,13 +3,451 @@ source: src/flake8_pytest_style/mod.rs
expression: checks
---
- kind:
UnittestAssertion: assertEqual
UnittestAssertion: assertTrue
location:
row: 9
column: 4
row: 11
column: 8
end_location:
row: 9
column: 20
row: 11
column: 23
fix:
content: assert expr is True
location:
row: 11
column: 8
end_location:
row: 11
column: 29
parent: ~
- kind:
UnittestAssertion: assertTrue
location:
row: 12
column: 8
end_location:
row: 12
column: 23
fix:
content: assert expr is True
location:
row: 12
column: 8
end_location:
row: 12
column: 34
parent: ~
- kind:
UnittestAssertion: assertTrue
location:
row: 13
column: 8
end_location:
row: 13
column: 23
fix:
content: assert expr is True
location:
row: 13
column: 8
end_location:
row: 13
column: 34
parent: ~
- kind:
UnittestAssertion: assertTrue
location:
row: 14
column: 8
end_location:
row: 14
column: 23
fix:
content: "assert expr is True, msg"
location:
row: 14
column: 8
end_location:
row: 14
column: 43
parent: ~
- kind:
UnittestAssertion: assertTrue
location:
row: 15
column: 8
end_location:
row: 15
column: 23
fix:
content: "assert expr is True, msg"
location:
row: 15
column: 8
end_location:
row: 15
column: 43
parent: ~
- kind:
UnittestAssertion: assertTrue
location:
row: 16
column: 8
end_location:
row: 16
column: 23
fix: ~
parent: ~
- kind:
UnittestAssertion: assertTrue
location:
row: 17
column: 8
end_location:
row: 17
column: 23
fix: ~
parent: ~
- kind:
UnittestAssertion: assertTrue
location:
row: 18
column: 8
end_location:
row: 18
column: 23
fix: ~
parent: ~
- kind:
UnittestAssertion: assertTrue
location:
row: 19
column: 8
end_location:
row: 19
column: 23
fix: ~
parent: ~
- kind:
UnittestAssertion: assertFalse
location:
row: 22
column: 8
end_location:
row: 22
column: 24
fix:
content: assert True is False
location:
row: 22
column: 8
end_location:
row: 22
column: 30
parent: ~
- kind:
UnittestAssertion: assertEqual
location:
row: 25
column: 8
end_location:
row: 25
column: 24
fix:
content: assert 1 == 2
location:
row: 25
column: 8
end_location:
row: 25
column: 30
parent: ~
- kind:
UnittestAssertion: assertNotEqual
location:
row: 28
column: 8
end_location:
row: 28
column: 27
fix:
content: assert 1 != 1
location:
row: 28
column: 8
end_location:
row: 28
column: 33
parent: ~
- kind:
UnittestAssertion: assertGreater
location:
row: 31
column: 8
end_location:
row: 31
column: 26
fix:
content: assert 1 > 2
location:
row: 31
column: 8
end_location:
row: 31
column: 32
parent: ~
- kind:
UnittestAssertion: assertGreaterEqual
location:
row: 34
column: 8
end_location:
row: 34
column: 31
fix:
content: assert 1 >= 2
location:
row: 34
column: 8
end_location:
row: 34
column: 37
parent: ~
- kind:
UnittestAssertion: assertLess
location:
row: 37
column: 8
end_location:
row: 37
column: 23
fix:
content: assert 2 < 1
location:
row: 37
column: 8
end_location:
row: 37
column: 29
parent: ~
- kind:
UnittestAssertion: assertLessEqual
location:
row: 40
column: 8
end_location:
row: 40
column: 28
fix:
content: assert 1 <= 2
location:
row: 40
column: 8
end_location:
row: 40
column: 34
parent: ~
- kind:
UnittestAssertion: assertIn
location:
row: 43
column: 8
end_location:
row: 43
column: 21
fix:
content: "assert 1 in [2, 3]"
location:
row: 43
column: 8
end_location:
row: 43
column: 32
parent: ~
- kind:
UnittestAssertion: assertNotIn
location:
row: 46
column: 8
end_location:
row: 46
column: 24
fix:
content: "assert 2 not in [2, 3]"
location:
row: 46
column: 8
end_location:
row: 46
column: 35
parent: ~
- kind:
UnittestAssertion: assertIsNone
location:
row: 49
column: 8
end_location:
row: 49
column: 25
fix:
content: assert 0 is None
location:
row: 49
column: 8
end_location:
row: 49
column: 28
parent: ~
- kind:
UnittestAssertion: assertIsNotNone
location:
row: 52
column: 8
end_location:
row: 52
column: 28
fix:
content: assert 0 is not None
location:
row: 52
column: 8
end_location:
row: 52
column: 31
parent: ~
- kind:
UnittestAssertion: assertIs
location:
row: 55
column: 8
end_location:
row: 55
column: 21
fix:
content: "assert [] is []"
location:
row: 55
column: 8
end_location:
row: 55
column: 29
parent: ~
- kind:
UnittestAssertion: assertIsNot
location:
row: 58
column: 8
end_location:
row: 58
column: 24
fix:
content: assert 1 is not 1
location:
row: 58
column: 8
end_location:
row: 58
column: 30
parent: ~
- kind:
UnittestAssertion: assertIsInstance
location:
row: 61
column: 8
end_location:
row: 61
column: 29
fix:
content: "assert isinstance(1, str)"
location:
row: 61
column: 8
end_location:
row: 61
column: 37
parent: ~
- kind:
UnittestAssertion: assertNotIsInstance
location:
row: 64
column: 8
end_location:
row: 64
column: 32
fix:
content: "assert not isinstance(1, int)"
location:
row: 64
column: 8
end_location:
row: 64
column: 40
parent: ~
- kind:
UnittestAssertion: assertRegex
location:
row: 67
column: 8
end_location:
row: 67
column: 24
fix:
content: "assert re.search(\"def\", \"abc\")"
location:
row: 67
column: 8
end_location:
row: 67
column: 39
parent: ~
- kind:
UnittestAssertion: assertNotRegex
location:
row: 70
column: 8
end_location:
row: 70
column: 27
fix:
content: "assert not re.search(\"abc\", \"abc\")"
location:
row: 70
column: 8
end_location:
row: 70
column: 42
parent: ~
- kind:
UnittestAssertion: assertRegexpMatches
location:
row: 73
column: 8
end_location:
row: 73
column: 32
fix:
content: "assert re.search(\"def\", \"abc\")"
location:
row: 73
column: 8
end_location:
row: 73
column: 47
parent: ~
- kind:
UnittestAssertion: assertNotRegex
location:
row: 76
column: 8
end_location:
row: 76
column: 27
fix:
content: "assert not re.search(\"abc\", \"abc\")"
location:
row: 76
column: 8
end_location:
row: 76
column: 42
parent: ~

View file

@ -3799,6 +3799,7 @@ impl CheckKind {
| CheckKind::TrueFalseComparison(..)
| CheckKind::TypeOfPrimitive(..)
| CheckKind::TypingTextStrAlias
| CheckKind::UnittestAssertion(..)
| CheckKind::UnnecessaryBuiltinImport(..)
| CheckKind::UnnecessaryCallAroundSorted(..)
| CheckKind::UnnecessaryCollectionCall(..)
@ -4045,6 +4046,9 @@ impl CheckKind {
CheckKind::UnnecessaryBuiltinImport(..) => {
Some("Remove unnecessary builtin import".to_string())
}
CheckKind::UnittestAssertion(assertion) => {
Some(format!("Replace `{assertion}(...)` with `assert ...`"))
}
CheckKind::UnnecessaryCallAroundSorted(func) => {
Some(format!("Remove unnecessary `{func}` call"))
}