mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-31 15:48:22 +00:00
Implement autofix for PT009 (#1713)
This commit is contained in:
parent
402feffe85
commit
07f72990a9
8 changed files with 946 additions and 56 deletions
|
@ -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 | |
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -18,3 +18,4 @@ mod marks;
|
|||
mod parametrize;
|
||||
mod patch;
|
||||
mod raises;
|
||||
mod unittest_assert;
|
||||
|
|
394
src/flake8_pytest_style/plugins/unittest_assert.rs
Normal file
394
src/flake8_pytest_style/plugins/unittest_assert.rs
Normal 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}`"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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: ~
|
||||
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue