Fix YTT201 for '!=' comparisons (#18293)

## Summary

Closes #18292.
This commit is contained in:
Charlie Marsh 2025-05-25 13:16:19 -04:00 committed by GitHub
parent 83a036960b
commit 14c3755445
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 20 additions and 9 deletions

View file

@ -52,17 +52,20 @@ impl Violation for SysVersionCmpStr3 {
/// ## What it does /// ## What it does
/// Checks for equality comparisons against the major version returned by /// Checks for equality comparisons against the major version returned by
/// `sys.version_info` (e.g., `sys.version_info[0] == 3`). /// `sys.version_info` (e.g., `sys.version_info[0] == 3` or `sys.version_info[0] != 3`).
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// Using `sys.version_info[0] == 3` to verify that the major version is /// Using `sys.version_info[0] == 3` to verify that the major version is
/// Python 3 or greater will fail if the major version number is ever /// Python 3 or greater will fail if the major version number is ever
/// incremented (e.g., to Python 4). This is likely unintended, as code /// incremented (e.g., to Python 4). This is likely unintended, as code
/// that uses this comparison is likely intended to be run on Python 2, /// that uses this comparison is likely intended to be run on Python 2,
/// but would now run on Python 4 too. /// but would now run on Python 4 too. Similarly, using `sys.version_info[0] != 3`
/// to check for Python 2 will also fail if the major version number is
/// incremented.
/// ///
/// Instead, use `>=` to check if the major version number is 3 or greater, /// Instead, use `>=` to check if the major version number is 3 or greater,
/// to future-proof the code. /// or `<` to check if the major version number is less than 3, to future-proof
/// the code.
/// ///
/// ## Example /// ## Example
/// ```python /// ```python
@ -88,12 +91,18 @@ impl Violation for SysVersionCmpStr3 {
/// - [Python documentation: `sys.version`](https://docs.python.org/3/library/sys.html#sys.version) /// - [Python documentation: `sys.version`](https://docs.python.org/3/library/sys.html#sys.version)
/// - [Python documentation: `sys.version_info`](https://docs.python.org/3/library/sys.html#sys.version_info) /// - [Python documentation: `sys.version_info`](https://docs.python.org/3/library/sys.html#sys.version_info)
#[derive(ViolationMetadata)] #[derive(ViolationMetadata)]
pub(crate) struct SysVersionInfo0Eq3; pub(crate) struct SysVersionInfo0Eq3 {
eq: bool,
}
impl Violation for SysVersionInfo0Eq3 { impl Violation for SysVersionInfo0Eq3 {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
if self.eq {
"`sys.version_info[0] == 3` referenced (python4), use `>=`".to_string() "`sys.version_info[0] == 3` referenced (python4), use `>=`".to_string()
} else {
"`sys.version_info[0] != 3` referenced (python4), use `<`".to_string()
}
} }
} }
@ -235,7 +244,7 @@ pub(crate) fn compare(checker: &Checker, left: &Expr, ops: &[CmpOp], comparators
{ {
if *i == 0 { if *i == 0 {
if let ( if let (
[CmpOp::Eq | CmpOp::NotEq], [operator @ (CmpOp::Eq | CmpOp::NotEq)],
[ [
Expr::NumberLiteral(ast::ExprNumberLiteral { Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(n), value: ast::Number::Int(n),
@ -246,7 +255,9 @@ pub(crate) fn compare(checker: &Checker, left: &Expr, ops: &[CmpOp], comparators
{ {
if *n == 3 && checker.enabled(Rule::SysVersionInfo0Eq3) { if *n == 3 && checker.enabled(Rule::SysVersionInfo0Eq3) {
checker.report_diagnostic(Diagnostic::new( checker.report_diagnostic(Diagnostic::new(
SysVersionInfo0Eq3, SysVersionInfo0Eq3 {
eq: matches!(*operator, CmpOp::Eq),
},
left.range(), left.range(),
)); ));
} }

View file

@ -20,7 +20,7 @@ YTT201.py:8:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=`
10 | PY2 = version_info[0] != 3 10 | PY2 = version_info[0] != 3
| |
YTT201.py:9:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=` YTT201.py:9:7: YTT201 `sys.version_info[0] != 3` referenced (python4), use `<`
| |
7 | PY3 = sys.version_info[0] == 3 7 | PY3 = sys.version_info[0] == 3
8 | PY3 = version_info[0] == 3 8 | PY3 = version_info[0] == 3
@ -29,7 +29,7 @@ YTT201.py:9:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=`
10 | PY2 = version_info[0] != 3 10 | PY2 = version_info[0] != 3
| |
YTT201.py:10:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=` YTT201.py:10:7: YTT201 `sys.version_info[0] != 3` referenced (python4), use `<`
| |
8 | PY3 = version_info[0] == 3 8 | PY3 = version_info[0] == 3
9 | PY2 = sys.version_info[0] != 3 9 | PY2 = sys.version_info[0] != 3