mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 14:21:24 +00:00
[pycodestyle
] Allow dtype
comparisons in type-comparison
(#9676)
## Summary Per https://github.com/astral-sh/ruff/issues/9570: > `dtype` are a bit of a strange beast, but definitely best thought of as instances, not classes, and they are meant to be comparable not just to their own class, but also to the corresponding scalar types (e.g., `x.dtype == np.float32`) and strings (e.g., `x.dtype == ['i1,i4']`; basically, `__eq__` always tries to do `dtype(other)`. This PR thus allows comparisons to `dtype` in preview. Closes https://github.com/astral-sh/ruff/issues/9570. ## Test Plan `cargo test`
This commit is contained in:
parent
50122d2308
commit
a6f7100b55
3 changed files with 46 additions and 0 deletions
|
@ -126,3 +126,15 @@ class Foo:
|
|||
# Okay
|
||||
if type(value) is str:
|
||||
...
|
||||
|
||||
|
||||
import numpy as np
|
||||
|
||||
#: Okay
|
||||
x.dtype == float
|
||||
|
||||
#: Okay
|
||||
np.dtype(int) == float
|
||||
|
||||
#: E721
|
||||
dtype == float
|
||||
|
|
|
@ -162,7 +162,14 @@ pub(crate) fn preview_type_comparison(checker: &mut Checker, compare: &ast::Expr
|
|||
.filter(|(_, op)| matches!(op, CmpOp::Eq | CmpOp::NotEq))
|
||||
.map(|((left, right), _)| (left, right))
|
||||
{
|
||||
// If either expression is a type...
|
||||
if is_type(left, checker.semantic()) || is_type(right, checker.semantic()) {
|
||||
// And neither is a `dtype`...
|
||||
if is_dtype(left, checker.semantic()) || is_dtype(right, checker.semantic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Disallow the comparison.
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
TypeComparison {
|
||||
preview: PreviewMode::Enabled,
|
||||
|
@ -295,3 +302,23 @@ fn is_type(expr: &Expr, semantic: &SemanticModel) -> bool {
|
|||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the [`Expr`] appears to be a reference to a NumPy dtype, since:
|
||||
/// > `dtype` are a bit of a strange beast, but definitely best thought of as instances, not
|
||||
/// > classes, and they are meant to be comparable not just to their own class, but also to the
|
||||
/// corresponding scalar types (e.g., `x.dtype == np.float32`) and strings (e.g.,
|
||||
/// `x.dtype == ['i1,i4']`; basically, __eq__ always tries to do `dtype(other)`).
|
||||
fn is_dtype(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
match expr {
|
||||
// Ex) `np.dtype(obj)`
|
||||
Expr::Call(ast::ExprCall { func, .. }) => semantic
|
||||
.resolve_call_path(func)
|
||||
.is_some_and(|call_path| matches!(call_path.as_slice(), ["numpy", "dtype"])),
|
||||
// Ex) `obj.dtype`
|
||||
Expr::Attribute(ast::ExprAttribute { attr, .. }) => {
|
||||
// Ex) `obj.dtype`
|
||||
attr.as_str() == "dtype"
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,4 +129,11 @@ E721.py:59:4: E721 Use `is` and `is not` for type comparisons, or `isinstance()`
|
|||
61 | #: Okay
|
||||
|
|
||||
|
||||
E721.py:140:1: E721 Use `is` and `is not` for type comparisons, or `isinstance()` for isinstance checks
|
||||
|
|
||||
139 | #: E721
|
||||
140 | dtype == float
|
||||
| ^^^^^^^^^^^^^^ E721
|
||||
|
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue